So, the 2-dimensional array (a matrix, no?) was the solution I was going to suggest, as well.
I am assuming, first of all, that this is some sort of robot/palletizing application with the option of storing 50 different patterns (or what I will call recipes).
Now, it seems that if a recipe number of less than 51 (i.e., recipes 1 through 50), the data associated with that recipe will be loaded into a working set of data for the robot to use. I wonder what would happen if recipe 51 is selected (perhaps an alarm is triggered to represent the selection is out of range). I also see that the data that is moved resides in registers 1 through 199 of each N-table. Is the 0 register (i.e., N153:0) used for anything, such as storing a pertinent recipe identifier?
If you don't need to store anything in registers 200-255 and/or if you do not need more than 50 recipes, it may be worth "trimming the fat" and reducing your array to an Int[51,200] to save on data consumption in the PLC. I am not sure if it will make processing faster (in other words, reduce your scan time), as well, since the PLC wouldn't need to navigate through such a large table when this code is called. That would depend on how the PLC navigates the table to parse the requested information.
The thing that caught my eye is that the recipe pointer value didn't match what your code should have resulted in (153 was displayed, yet the math should have resulted in 103), so I am assuming this code resides in some sort of subroutine or section of code that was not being actively called and N20:99 is used in other parts of the code, hence the unexpected value.
So, you are probably fine with what you have, but if you would like to clean it up a bit, explore reducing the size of your 2-dimensional array. If you find that you need that amount of data for reasons not conveyed to us, then you should be good to go.
This was the example I had come up for you: