A friend was in need of an algorithm that would let him loop through the elements of an NxM matrix (N and M are odd). I came up with a solution, but I wanted to see if my fellow SO'ers could come up with a better solution.
I'm posting my solution as an answer to this question.
Example Output:
For a 3x3 matrix, the output should be:
(0, 0) (1, 0) (1, 1) (0, 1) (-1, 1) (-1, 0) (-1, -1) (0, -1) (1, -1)
Furthermore, the algorithm should support non-square matrices, so for example for a 5x3 matrix, the output should be:
(0, 0) (1, 0) (1, 1) (0, 1) (-1, 1) (-1, 0) (-1, -1) (0, -1) (1, -1) (2, -1) (2, 0) (2, 1) (-2, 1) (-2, 0) (-2, -1)
Here's a JavaScript (ES6) iterative solution to this problem:
Here's how to use it:
spiralMatrix(0, 0, 1, 100);
This will create an outward spiral, starting at coordinates (x = 0, y = 0) with step of 1 and a total number of items equals to 100. The implementation always starts the movement in the following order - up, right, bottom, left.
Please, note that this implementation creates square matrices.
Here's a O(1) solution to find the position in a squared spiral : Fiddle
Here's an answer in Julia: my approach is to assign the points in concentric squares ('spirals') around the origin
(0,0)
, where each square has side lengthm = 2n + 1
, to produce an ordered dictionary with location numbers (starting from 1 for the origin) as keys and the corresponding coordinate as value.Since the maximum location per spiral is at
(n,-n)
, the rest of the points can be found by simply working backward from this point, i.e. from the bottom right corner bym-1
units, then repeating for the perpendicular 3 segments ofm-1
units.This process is written in reverse order below, corresponding to how the spiral proceeds rather than this reverse counting process, i.e. the
ra
[right ascending] segment is decremented by3(m+1)
, thenla
[left ascending] by2(m+1)
, and so on - hopefully this is self-explanatory.So for your first example, plugging
m = 3
into the equation to find n givesn = (5-1)/2 = 2
, andwalk(2)
gives an ordered dictionary of locations to coordinates, which you can turn into just an array of coordinates by accessing the dictionary'svals
field:Note that for some functions [e.g.
norm
] it can be preferable to leave the coordinates in arrays rather thanTuple{Int,Int}
, but here I change them into tuples—(x,y)
—as requested, using list comprehension.The context for "supporting" a non-square matrix isn't specified (note that this solution still calculates the off-grid values), but if you want to filter to only the range
x
byy
(here forx=5
,y=3
) after calculating the full spiral thenintersect
this matrix against the values fromwalk
.Here's a solution in Python 3 for printing consecutive integers in a spiral clockwise and counterclockwise.
Explanation
A spiral is made of concentric squares, for instance a 5x5 square with clockwise rotation looks like this:
(
>>>>>
means "go 5 times right" or increase column index 5 times,v
means down or increase row index, etc.)All squares are the same up to their size, I looped over the concentric squares.
For each square the code has four loops (one for each side), in each loop we increase or decrease the columns or row index. If
i
is the row index andj
the column index then a 5x5 square can be constructed by: - incrementingj
from 0 to 4 (5 times) - incrementingi
from 1 to 4 (4 times) - decrementingj
from 3 to 0 (4 times) - decrementingi
from 3 to 1 (3 times)For the next squares (3x3 and 1x1) we do the same but shift the initial and final indices appropriately. I used an index
k
for each concentric square, there are n//2 + 1 concentric squares.Finally, some math for pretty-printing.
To print the indexes:
Here is my solution (In Ruby)
This is a slightly different version - trying to use
recursion
anditerators
in LUA. At each step the program descends further inside the matrix and loops. I also added an extra flag to spiralclockwise
oranticlockwise
. The output starts from the bottom right corners and loops recursively towards the center.