Looping in a spiral
Asked Answered
P

35

179

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)

3x3 matrix

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)

5x3 matrix

Paraclete answered 29/12, 2008 at 18:40 Comment(9)
Can you explain what you want for non-square matrices? Your solution has a "jump" from (2,1) to (-2,1) -- is this intended? [E.g. for a 7x3 matrix, it would have two more "jumps", and for a (2k+1)x3 matrix it would have 2k-3 jumps?]Superdominant
Yes, the jumps are intentional. I've updated the question with a 5x3 matrix image. As you can see from the image, we're skipping the top and bottom rows.Edithe
Ok, then your own code seems cleanest. And although this is offtopic: how did you generate those images? :)Superdominant
=)) I did not generate them. In fact, the way I created them is quite stupid. I created the tables in OO.org Calc, took a screenshot, and edited the screenshot in GIMP. =))Edithe
Why would you want to do this? Iterating across rows/columns has WAY better cache behaviour...locality of data!Nitrate
@Ying: I don't really know why my friend needs this, but he said he wants to favor members of the matrix closer to the center in a search algorithm.Edithe
@Ying, he might stop the search as soon as he locates something. Maybe for a game.Luxemburg
I've removed the code-golf tag. It doesn't seem to be code golf.Broder
FYI, you can calculate the position for a single cell without loops: #9136323Orvah
P
74

Here's my solution (in Python):

def spiral(X, Y):
    x = y = 0
    dx = 0
    dy = -1
    for i in range(max(X, Y)**2):
        if (-X/2 < x <= X/2) and (-Y/2 < y <= Y/2):
            print (x, y)
            # DO STUFF...
        if x == y or (x < 0 and x == -y) or (x > 0 and x == 1-y):
            dx, dy = -dy, dx
        x, y = x+dx, y+dy
Paraclete answered 29/12, 2008 at 18:41 Comment(5)
This is the best way of writing it, as far as I can see. The only possible improvement would be to make it O(MN) instead of O(max(M,N)^2) by directly skipping past those (x,y) that are not going to be printed, but that will make the code a bit more ugly.Superdominant
I'm optimizing my solution and it's pretty close to what you've already got. This is a pretty good solution I think. Besides ShreevatsaR's suggestion, and stuff like not calculating x/2 and y/2 each iteration, there's not too much to improve on except style.Ornie
Any solutions for matlab?!Abbieabbot
Does this give for good cache coherence for accessing image buffer data? (There are so many answers here, but not much info regarding which works best for high performance image operations)Joellajoelle
@Joellajoelle - that doesn't come into play, because the result is always the same spiral pattern of coordinates. Whether the spiral pattern is cache coherent I guess is dependent on the image buffer implementation. (my guess is it would thrash the cache more than other ways of walking the image, like going line by line in order). But the choice of algorithm to produce these coordinate's probably won't affect the cache.Selenography
B
37

C++ anyone? Quick translation from python, posted for completeness

void Spiral( int X, int Y){
    int x,y,dx,dy;
    x = y = dx =0;
    dy = -1;
    int t = std::max(X,Y);
    int maxI = t*t;
    for(int i =0; i < maxI; i++){
        if ((-X/2 <= x) && (x <= X/2) && (-Y/2 <= y) && (y <= Y/2)){
            // DO STUFF...
        }
        if( (x == y) || ((x < 0) && (x == -y)) || ((x > 0) && (x == 1-y))){
            t = dx;
            dx = -dy;
            dy = t;
        }
        x += dx;
        y += dy;
    }
}
Broach answered 12/10, 2009 at 15:29 Comment(2)
you can also use s and ds like I do to detect the corners which gets rid of the huge if conditionTestudo
An edit to this post was suggested here. Although the edit was rejected because it changes the meaning of your post, you might want to consider incorporating the suggested changes if it makes sense to do so.Floorage
D
28
let x = 0
let y = 0
let d = 1
let m = 1

while true
  while 2 * x * d < m
    print(x, y)
    x = x + d
  while 2 * y * d < m
    print(x, y)
    y = y + d
  d = -1 * d
  m = m + 1

There have been many proposed solutions for this problem wrote in various programming languages however they all seem to stem from the same convoluted approach. I'm going to consider the more general problem of computing a spiral which can be expressed concisely using induction.

Base case: Start at (0, 0), move forward 1 square, turn left, move forward 1 square, turn left. Inductive step: Move forward n+1 squares, turn left, move forward n+1 squares, turn left.

The mathematical elegance of expressing this problem strongly suggests there should be a simple algorithm to compute the solution. Keeping abstraction in mind, I've chosen not to implement the algorithm in a specific programming language but rather as pseudo-code.

First I'll consider an algorithm to compute just 2 iterations of the spiral using 4 pairs of while loops. The structure of each pair is similar, yet distinct in its own right. This may seem crazy at first (some loops only get executed once) but step by step I'll make transformations until we arrive at 4 pairs of loops that are identical and hence can be replaced with a single pair placed inside of another loop. This will provide us with a general solution of computing n iterations without using any conditionals.

let x = 0
let y = 0

//RIGHT, UP
while x < 1
  print(x, y)
  x = x + 1
while y < 1
  print(x, y)
  y = y + 1

//LEFT, LEFT, DOWN, DOWN
while x > -1
  print(x, y)
  x = x - 1
while y > -1
  print(x, y)
  y = y - 1

//RIGHT, RIGHT, RIGHT, UP, UP, UP
while x < 2
  print(x, y)
  x = x + 1
while y < 2
  print(x, y)
  y = y + 1

//LEFT, LEFT, LEFT, LEFT, DOWN, DOWN, DOWN, DOWN
while x > -2
  print(x, y)
  x = x - 1
while y > -2
  print(x, y)
  y = y - 1

The first transformation we will make is the introduction of a new variable d, for direction, that holds either the value +1 or -1. The direction switches after each pair of loops. Since we know the value of d at all points, we can multiply each side of each inequality by it, adjust the direction of the inequality accordingly and simplify any multiplications of d by a constant to another constant. This leaves us with the following.

let x = 0
let y = 0
let d = 1

//RIGHT, UP
while x * d < 1
  print(x, y)
  x = x + d
while y * d < 1
  print(x, y)
  y = y + d
d = -1 * d

//LEFT, LEFT, DOWN, DOWN
while x * d < 1
  print(x, y)
  x = x + d
while y * d < 1
  print(x, y)
  y = y + d
d = -1 * d

//RIGHT, RIGHT, RIGHT, UP, UP, UP
while x * d < 2
  print(x, y)
  x = x + d
while y * d < 2
  print(x, y)
  y = y + d
d = -1 * d

//LEFT, LEFT, LEFT, LEFT, DOWN, DOWN, DOWN, DOWN
while x * d < 2
  print(x, y)
  x = x + d
while y * d < 2
  print(x, y)
  y = y + d

Now we note that both x * d and the RHS are integers so we can subtract any real value between 0 and 1 from the RHS without affecting the result of the inequality. We choose to subtract 0.5 from the inequalities of every other pair of while loops in order to establish more of a pattern.

let x = 0
let y = 0
let d = 1

//RIGHT, UP
while x * d < 0.5
  print(x, y)
  x = x + d
while y * d < 0.5
  print(x, y)
  y = y + d
d = -1 * d

//LEFT, LEFT, DOWN, DOWN
while x * d < 1
  print(x, y)
  x = x + d
while y * d < 1
  print(x, y)
  y = y + d
d = -1 * d

//RIGHT, RIGHT, RIGHT, UP, UP, UP
while x * d < 1.5
  print(x, y)
  x = x + d
while y * d < 1.5
  print(x, y)
  y = y + d
d = -1 * d

//LEFT, LEFT, LEFT, LEFT, DOWN, DOWN, DOWN, DOWN
while x * d < 2
  print(x, y)
  x = x + d
while y * d < 2
  print(x, y)
  y = y + d

We can now introduce another variable m for the number of steps we take at each pair of while loops.

let x = 0
let y = 0
let d = 1
let m = 0.5

//RIGHT, UP
while x * d < m
  print(x, y)
  x = x + d
while y * d < m
  print(x, y)
  y = y + d
d = -1 * d
m = m + 0.5

//LEFT, LEFT, DOWN, DOWN
while x * d < m
  print(x, y)
  x = x + d
while y * d < m
  print(x, y)
  y = y + d
d = -1 * d
m = m + 0.5

//RIGHT, RIGHT, RIGHT, UP, UP, UP
while x * d < m
  print(x, y)
  x = x + d
while y * d < m
  print(x, y)
  y = y + d
d = -1 * d
m = m + 0.5

//LEFT, LEFT, LEFT, LEFT, DOWN, DOWN, DOWN, DOWN
while x * d < m
  print(x, y)
  x = x + d
while y * d < m
  print(x, y)
  y = y + d

Finally, we see that the structure of each pair of while loops is identical and can be reduced to a single loop placed inside of another loop. Also, to avoid using real valued numbers I've multiplied the initial value of m; the value m is incremented by; and both sides of each inequality by 2.

This leads to the solution shown at the beginning of this answer.

EDIT: It has been a few years but I had a similar problem and wrote the following solution in F# which I want to share. The word print may have been a misnomer in my original answer but hopefully this non-pseudocode version will address any points raised in the comments regarding versatility and termination conditions. I have added example use cases for spiralling about an arbitrary point and finding the correct solution to the original problem for iterating an NxM matrix.

let spiral =
    let rec f (x, y) d m = seq {
        let mutable x = x
        let mutable y = y
        while 2 * x * d < m do
            yield x, y
            x <- x + d
        while 2 * y * d < m do
            yield x, y
            y <- y + d
        yield! f (x, y) -d (m + 1)
    }
    f (0, 0) 1 1

spiral
|> Seq.take 5
|> List.ofSeq;;
// [(0, 0); (1, 0); (1, 1); (0, 1); (-1, 1)]

spiral
|> Seq.take 5
|> Seq.map (fun (x, y) -> x + 5, y + 5)
|> List.ofSeq;;
// [(5, 5); (6, 5); (6, 6); (5, 6); (4, 6)]

spiral
|> Seq.takeWhile (fun (x, y) -> x * x + y * y < 9)
|> Seq.filter (fun (x, y) -> -2 <= x && x <= 2 && -1 <= y && y <= 1)
|> List.ofSeq;;
// [(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)]
Doi answered 10/11, 2015 at 21:22 Comment(9)
Under what conditions would your final solution terminate?Opalescent
What is the application of such type of pattern printing?Tim
@MerlynMorgan-Graham It terminates when the computer runs out of memory or power.Doi
It seems that the elegance of that solution stems from ignoring time and memory constraints. I recommend elegantly adding a termination condition (if possible). I also recommend moving it to the top of the answer, and showing the derivation below it.Opalescent
@AshishShukla Ask the OP. I think many other answers were quick to blindly copy a solution without taking a moment to comprehend the more fundamental problem - iterating in a square spiral. An elegant problem (almost) always has an elegant solution. I'm sure it's not too tricky to take my answer and implement it in whatever language one requires. Perhaps a good use for it would be in calculating the Ulman spiral.Doi
As a variation on the theme, it would be nice to have a function like {i,j}= getSpiralPosition(x,y,n) where n is the movement index, so therefore you can move in in a spiral about the point (x,y) by repeatedly calling the function with n++. The advantage is that the print(x,y) statement (or whatever) is taken out of the function and the function is therefore more versatile.Alonzoaloof
While the original question was about an NxM matrix, this is actually a very useful answer if you need to endlessly spiral-outward until you find something (ie then break or return). Of course, like the other comments noted, you do need to define that termination condition or it will run forever.Superscription
There's something off about this - but I haven't had enough caffeine this morning to put my finger on it. Since d just flops between 1 and -1, x and y just keep going between 0 and 1 and don't increment beyond that constraint. But it seems right given the value of m. HmmmmTeryl
@Mike: This is the right answer. Simple and efficient. Thank youSaldivar
K
22

Here's a O(1) solution to find the position in a squared spiral : Fiddle

function spiral(n) {
    // given n an index in the squared spiral
    // p the sum of point in inner square
    // a the position on the current square
    // n = p + a

    var r = Math.floor((Math.sqrt(n + 1) - 1) / 2) + 1;

    // compute radius : inverse arithmetic sum of 8+16+24+...=
    var p = (8 * r * (r - 1)) / 2;
    // compute total point on radius -1 : arithmetic sum of 8+16+24+...

    var en = r * 2;
    // points by face

    var a = (1 + n - p) % (r * 8);
    // compute de position and shift it so the first is (-r,-r) but (-r+1,-r)
    // so square can connect

    var pos = [0, 0, r];
    switch (Math.floor(a / (r * 2))) {
        // find the face : 0 top, 1 right, 2, bottom, 3 left
        case 0:
            {
                pos[0] = a - r;
                pos[1] = -r;
            }
            break;
        case 1:
            {
                pos[0] = r;
                pos[1] = (a % en) - r;

            }
            break;
        case 2:
            {
                pos[0] = r - (a % en);
                pos[1] = r;
            }
            break;
        case 3:
            {
                pos[0] = -r;
                pos[1] = r - (a % en);
            }
            break;
    }
    console.log("n : ", n, " r : ", r, " p : ", p, " a : ", a, "  -->  ", pos);
    return pos;
}
Kuhn answered 10/10, 2013 at 5:25 Comment(2)
To start from the center add two lines. if (n === 0) return [0, 0, r]; --n; See Fiddle: jsfiddle.net/Wishmesh/nwd9gt1s/2Marillin
Any idea on how to reverse this algorithm? I have pos[0] and pos[1] as an input and would like to get nJohanna
L
16

I love python's generators.

def spiral(N, M):
    x,y = 0,0   
    dx, dy = 0, -1

    for dumb in xrange(N*M):
        if abs(x) == abs(y) and [dx,dy] != [1,0] or x>0 and y == 1-x:  
            dx, dy = -dy, dx            # corner, change direction

        if abs(x)>N/2 or abs(y)>M/2:    # non-square
            dx, dy = -dy, dx            # change direction
            x, y = -y+dx, x+dy          # jump

        yield x, y
        x, y = x+dx, y+dy

Testing with:

print 'Spiral 3x3:'
for a,b in spiral(3,3):
    print (a,b),

print '\n\nSpiral 5x3:'
for a,b in spiral(5,3):
    print (a,b),

You get:

Spiral 3x3:
(0, 0) (1, 0) (1, 1) (0, 1) (-1, 1) (-1, 0) (-1, -1) (0, -1) (1, -1) 

Spiral 5x3:
(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)
Legalize answered 28/7, 2009 at 21:42 Comment(0)
R
11

Here's a C++ solution that shows that you can calculate the next (x, y) coordinates directly and easily from the previous ones - no need for tracking the current direction, radius, or anything else:

void spiral(const int M, const int N)
{
    // Generate an Ulam spiral centered at (0, 0).
    int x = 0;
    int y = 0;

    int end = max(N, M) * max(N, M);
    for(int i = 0; i < end; ++i)
    {
        // Translate coordinates and mask them out.
        int xp = x + N / 2;
        int yp = y + M / 2;
        if(xp >= 0 && xp < N && yp >= 0 && yp < M)
            cout << xp << '\t' << yp << '\n';

        // No need to track (dx, dy) as the other examples do:
        if(abs(x) <= abs(y) && (x != y || x >= 0))
            x += ((y >= 0) ? 1 : -1);
        else
            y += ((x >= 0) ? -1 : 1);
    }
}

If all you're trying to do is generate the first N points in the spiral (without the original problem's constraint of masking to an N x M region), the code becomes very simple:

void spiral(const int N)
{
    int x = 0;
    int y = 0;
    for(int i = 0; i < N; ++i)
    {
        cout << x << '\t' << y << '\n';
        if(abs(x) <= abs(y) && (x != y || x >= 0))
            x += ((y >= 0) ? 1 : -1);
        else
            y += ((x >= 0) ? -1 : 1);
    }
}

The trick is that you can compare x and y to determine what side of the square you're on, and that tells you what direction to move in.

Render answered 6/8, 2015 at 20:2 Comment(0)
E
8

Java spiral "Code golf" attempt, based on the C++ variant.

public static void Spiral(int X, int Y) {
    int x=0, y=0, dx = 0, dy = -1;
    int t = Math.max(X,Y);
    int maxI = t*t;

    for (int i=0; i < maxI; i++){
        if ((-X/2 <= x) && (x <= X/2) && (-Y/2 <= y) && (y <= Y/2)) {
            System.out.println(x+","+y);
            //DO STUFF
        }

        if( (x == y) || ((x < 0) && (x == -y)) || ((x > 0) && (x == 1-y))) {
            t=dx; dx=-dy; dy=t;
        }   
        x+=dx; y+=dy;
    }
}
Ette answered 15/5, 2012 at 18:56 Comment(0)
S
6

TDD, in Java.

SpiralTest.java:

import java.awt.Point;
import java.util.List;

import junit.framework.TestCase;

public class SpiralTest extends TestCase {

    public void test3x3() throws Exception {
        assertEquals("(0, 0) (1, 0) (1, 1) (0, 1) (-1, 1) (-1, 0) (-1, -1) (0, -1) (1, -1)", strung(new Spiral(3, 3).spiral()));
    }

    public void test5x3() throws Exception {
        assertEquals("(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)",
                strung(new Spiral(5, 3).spiral()));
    }

    private String strung(List<Point> points) {
        StringBuffer sb = new StringBuffer();
        for (Point point : points)
            sb.append(strung(point));
        return sb.toString().trim();
    }

    private String strung(Point point) {
        return String.format("(%s, %s) ", point.x, point.y);
    }

}

Spiral.java:

import java.awt.Point;
import java.util.ArrayList;
import java.util.List;

public class Spiral {
    private enum Direction {
    E(1, 0) {Direction next() {return N;}},
    N(0, 1) {Direction next() {return W;}},
    W(-1, 0) {Direction next() {return S;}},
    S(0, -1) {Direction next() {return E;}},;

        private int dx;
        private int dy;

        Point advance(Point point) {
            return new Point(point.x + dx, point.y + dy);
        }

        abstract Direction next();

        Direction(int dx, int dy) {
            this.dx = dx;
            this.dy = dy;
        }
    };
    private final static Point ORIGIN = new Point(0, 0);
    private final int   width;
    private final int   height;
    private Point       point;
    private Direction   direction   = Direction.E;
    private List<Point> list = new ArrayList<Point>();

    public Spiral(int width, int height) {
        this.width = width;
        this.height = height;
    }

    public List<Point> spiral() {
        point = ORIGIN;
        int steps = 1;
        while (list.size() < width * height) {
            advance(steps);
            advance(steps);
            steps++;
        }
        return list;
    }

    private void advance(int n) {
        for (int i = 0; i < n; ++i) {
            if (inBounds(point))
                list.add(point);
            point = direction.advance(point);
        }
        direction = direction.next();
    }

    private boolean inBounds(Point p) {
        return between(-width / 2, width / 2, p.x) && between(-height / 2, height / 2, p.y);
    }

    private static boolean between(int low, int high, int n) {
        return low <= n && n <= high;
    }
}
Stiver answered 28/7, 2009 at 20:34 Comment(1)
@leppie: Maybe not - certainly not short enough - but I think it's a good demonstration of TDD, and reasonably clean, easy-to-understand, correct code. I'll leave it in.Stiver
G
4

Here is my solution (In Ruby)

def spiral(xDim, yDim)
   sx = xDim / 2
   sy = yDim / 2

   cx = cy = 0
   direction = distance = 1

   yield(cx,cy)
   while(cx.abs <= sx || cy.abs <= sy)
      distance.times { cx += direction; yield(cx,cy) if(cx.abs <= sx && cy.abs <= sy); } 
      distance.times { cy += direction; yield(cx,cy) if(cx.abs <= sx && cy.abs <= sy); } 
      distance += 1
      direction *= -1
   end
end

spiral(5,3) { |x,y|
   print "(#{x},#{y}),"
}
Gilda answered 29/12, 2008 at 20:7 Comment(2)
Still O(max(n,m)^2), but nice style.Ornie
direction=-direction instead of direction*=-1? if you were golfing d=-d is shorter than d*=-1 tooTestudo
S
4

Haskell, take your pick:

spiral x y = (0, 0) : concatMap ring [1 .. max x' y'] where
    ring n | n > x' = left x' n  ++ right x' (-n)
    ring n | n > y' = up   n  y' ++ down (-n) y'
    ring n          = up n n ++ left n n ++ down n n ++ right n n
    up    x y = [(x, n) | n <- [1-y .. y]]; down = (.) reverse . up
    right x y = [(n, y) | n <- [1-x .. x]]; left = (.) reverse . right
    (x', y') = (x `div` 2, y `div` 2)

spiral x y = filter (\(x',y') -> 2*abs x' <= x && 2*abs y' <= y) .
             scanl (\(a,b) (c,d) -> (a+c,b+d)) (0,0) $
             concat [ (:) (1,0) . tail 
                    $ concatMap (replicate n) [(0,1),(-1,0),(0,-1),(1,0)]
                    | n <- [2,4..max x y] ]
Surefire answered 3/6, 2009 at 21:45 Comment(5)
Please don't take this as a rant or a troll's comment, but GOD is haskell ugly!Uncloak
I could not agree with the above comment more.Peeress
This Haskell looks very trendy to me.Broder
Yes, but note how expressive it is. Compare its length with some of the other examples posted here.Floorage
@Uncloak Actually, it's not the best solution in Haskell. Take a look here: rosettacode.org/wiki/Spiral_matrix#HaskellVigor
S
4

Your question looks like a question called spiral memory. In that problem, each square on the grid is allocated in a spiral pattern starting from the number 1 which locates at the origin. And then counting up while spiraling outwards. For example:

17  16  15  14  13

18   5   4   3  12

19   6   1   2  11

20   7   8   9  10

21  22  23  ---->

My solution for computing the coordinates of each number following this spiral pattern is posted below:

def spiral_pattern(num):
    x = y = 0
    for _ in range(num-1):
        x, y = find_next(x, y)
    yield (x, y)


def find_next(x, y):
    """find the coordinates of the next number"""
    if x == 0 and y == 0:
        return 1, 0

    if abs(x) == abs(y):
        if x > 0 and y > 0:
            x, y = left(x, y)
        elif x < 0 and y > 0:
            x, y = down(x, y)
        elif x < 0 and y < 0:
            x, y = right(x, y)
        elif x > 0 and y < 0:
            x, y = x+1, y
    else:
        if x > y and abs(x) > abs(y):
            x, y = up(x, y)
        elif x < y and abs(x) < abs(y):
            x, y = left(x, y)
        elif x < y and abs(x) > abs(y):
            x, y = down(x, y)
        elif x > y and abs(x) < abs(y):
            x, y = right(x, y)

    return x, y

def up(x, y):
    return x, y+1


def down(x, y):
    return x, y-1


def left(x, y):
    return x-1, y


def right(x, y):
    return x+1, y
Scathe answered 13/6, 2019 at 6:49 Comment(0)
W
2

This is in C.

I happened to choose bad variable names. In the names T == top, L == left, B == bottom, R == right. So, tli is top left i and brj is bottom right j.

#include<stdio.h>

typedef enum {
   TLTOR = 0,
   RTTOB,
   BRTOL,
   LBTOT
} Direction;

int main() {
   int arr[][3] = {{1,2,3},{4,5,6}, {7,8,9}, {10,11,12}};
   int tli = 0, tlj = 0, bri = 3, brj = 2;
   int i;
   Direction d = TLTOR;

   while (tli < bri || tlj < brj) {
     switch (d) {
     case TLTOR:
    for (i = tlj; i <= brj; i++) {
       printf("%d ", arr[tli][i]);
    }
    tli ++;
    d = RTTOB;
    break;
     case RTTOB:
    for (i = tli; i <= bri; i++) {
       printf("%d ", arr[i][brj]);
    }
    brj --;
    d = BRTOL;
    break;
     case BRTOL:
    for (i = brj; i >= tlj; i--) {
       printf("%d ", arr[bri][i]);
    }
    bri --;
        d = LBTOT;
    break;
     case LBTOT:
    for (i = bri; i >= tli; i--) {
       printf("%d ", arr[i][tlj]);
    }
    tlj ++;
        d = TLTOR;
    break;
 }
   }
   if (tli == bri == tlj == brj) {
      printf("%d\n", arr[tli][tlj]);
   }
}
Widmer answered 9/7, 2012 at 23:58 Comment(0)
C
2

I have an open source library, pixelscan, that is a python library that provides functions to scan pixels on a grid in a variety of spatial patterns. Spatial patterns included are circular, rings, grids, snakes, and random walks. There are also various transformations (e.g., clip, swap, rotate, translate). The original OP problem can be solved as follows

for x, y in clip(swap(ringscan(0, 0, 0, 2)), miny=-1, maxy=1):
    print x, y

which yields the points

(0,0) (1,0) (1,1) (0,1) (-1,1) (-1,0) (-1,-1) (0,-1) (1,-1) (2,0) (2,1) (-2,1) (-2,0)
(-2,-1) (2,-1)

The libraries generators and transformations can be chained to change the points in a wide variety of orders and spatial patterns.

Cerebration answered 9/8, 2015 at 2:45 Comment(0)
P
2

Here's a JavaScript (ES6) iterative solution to this problem:

let spiralMatrix = (x, y, step, count) => {
    let distance = 0;
    let range = 1;
    let direction = 'up';

    for ( let i = 0; i < count; i++ ) {
        console.log('x: '+x+', y: '+y);
        distance++;
        switch ( direction ) {
            case 'up':
                y += step;
                if ( distance >= range ) {
                    direction = 'right';
                    distance = 0;
                }
                break;
            case 'right':
                x += step;
                if ( distance >= range ) {
                    direction = 'bottom';
                    distance = 0;
                    range += 1;
                }
                break;
            case 'bottom':
                y -= step;
                if ( distance >= range ) {
                    direction = 'left';
                    distance = 0;
                }
                break;
            case 'left':
                x -= step;
                if ( distance >= range ) {
                    direction = 'up';
                    distance = 0;
                    range += 1;
                }
                break;
            default:
                break;
        }
    }
}

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.

Pasteboard answered 20/10, 2017 at 15:6 Comment(0)
H
2

Here's a solution in Python 3 for printing consecutive integers in a spiral clockwise and counterclockwise.

import math

def sp(n): # spiral clockwise
    a=[[0 for x in range(n)] for y in range(n)]
    last=1
    for k in range(n//2+1):
      for j in range(k,n-k):
          a[k][j]=last
          last+=1
      for i in range(k+1,n-k):
          a[i][j]=last
          last+=1
      for j in range(n-k-2,k-1,-1):
          a[i][j]=last
          last+=1
      for i in range(n-k-2,k,-1):
          a[i][j]=last
          last+=1

    s=int(math.log(n*n,10))+2 # compute size of cell for printing
    form="{:"+str(s)+"}"
    for i in range(n):
        for j in range(n):
            print(form.format(a[i][j]),end="")
        print("")

sp(3)
# 1 2 3
# 8 9 4
# 7 6 5

sp(4)
#  1  2  3  4
# 12 13 14  5
# 11 16 15  6
# 10  9  8  7

def sp_cc(n): # counterclockwise
    a=[[0 for x in range(n)] for y in range(n)]
    last=1
    for k in range(n//2+1):
      for j in range(n-k-1,k-1,-1):
          a[n-k-1][j]=last
          last+=1
      for i in range(n-k-2,k-1,-1):
          a[i][j]=last
          last+=1
      for j in range(k+1,n-k):
          a[i][j]=last
          last+=1
      for i in range(k+1,n-k-1):
          a[i][j]=last
          last+=1

    s=int(math.log(n*n,10))+2 # compute size of cell for printing
    form="{:"+str(s)+"}"
    for i in range(n):
        for j in range(n):
            print(form.format(a[i][j]),end="")
        print("")

sp_cc(5)
#  9 10 11 12 13
#  8 21 22 23 14
#  7 20 25 24 15
#  6 19 18 17 16
#  5  4  3  2  1

Explanation

A spiral is made of concentric squares, for instance a 5x5 square with clockwise rotation looks like this:

 5x5        3x3      1x1

>>>>>
^   v       >>>
^   v   +   ^ v   +   >
^   v       <<<
<<<<v

(>>>>> 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 and j the column index then a 5x5 square can be constructed by: - incrementing j from 0 to 4 (5 times) - incrementing i from 1 to 4 (4 times) - decrementing j from 3 to 0 (4 times) - decrementing i 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:

def spi_cc(n): # counter-clockwise
    a=[[0 for x in range(n)] for y in range(n)]
    ind=[]
    last=n*n
    for k in range(n//2+1):
      for j in range(n-k-1,k-1,-1):
          ind.append((n-k-1,j))
      for i in range(n-k-2,k-1,-1):
          ind.append((i,j))
      for j in range(k+1,n-k):
          ind.append((i,j))
      for i in range(k+1,n-k-1):
          ind.append((i,j))

    print(ind)

spi_cc(5)
Hobnob answered 4/3, 2018 at 15:11 Comment(0)
D
1

Here's c#, linq'ish.

public static class SpiralCoords
{
  public static IEnumerable<Tuple<int, int>> GenerateOutTo(int radius)
  {
    //TODO trap negative radius.  0 is ok.

    foreach(int r in Enumerable.Range(0, radius + 1))
    {
      foreach(Tuple<int, int> coord in GenerateRing(r))
      {
        yield return coord;
      }
    }
  }

  public static IEnumerable<Tuple<int, int>> GenerateRing(int radius)
  {
    //TODO trap negative radius.  0 is ok.

    Tuple<int, int> currentPoint = Tuple.Create(radius, 0);
    yield return Tuple.Create(currentPoint.Item1, currentPoint.Item2);

    //move up while we can
    while (currentPoint.Item2 < radius)
    {
      currentPoint.Item2 += 1;
      yield return Tuple.Create(currentPoint.Item1, currentPoint.Item2);
    }
    //move left while we can
    while (-radius < currentPoint.Item1)
    {
      currentPoint.Item1 -=1;
      yield return Tuple.Create(currentPoint.Item1, currentPoint.Item2);    
    }
    //move down while we can
    while (-radius < currentPoint.Item2)
    {
      currentPoint.Item2 -= 1;
      yield return Tuple.Create(currentPoint.Item1, currentPoint.Item2);
    }
    //move right while we can
    while (currentPoint.Item1 < radius)
    {
      currentPoint.Item1 +=1;
      yield return Tuple.Create(currentPoint.Item1, currentPoint.Item2);    
    }
    //move up while we can
    while (currentPoint.Item2 < -1)
    {
      currentPoint.Item2 += 1;
      yield return Tuple.Create(currentPoint.Item1, currentPoint.Item2);
    }
  }

}

The question's first example (3x3) would be:

var coords = SpiralCoords.GenerateOutTo(1);

The question's second example (5x3) would be:

var coords = SpiralCoords.GenerateOutTo(2).Where(x => abs(x.Item2) < 2);
Delora answered 6/9, 2012 at 18:35 Comment(0)
G
1

This is a slightly different version - trying to use recursion and iterators in LUA. At each step the program descends further inside the matrix and loops. I also added an extra flag to spiral clockwise or anticlockwise. The output starts from the bottom right corners and loops recursively towards the center.

local row, col, clockwise

local SpiralGen
SpiralGen = function(loop)  -- Generator of elements in one loop
    local startpos = { x = col - loop, y = row - loop }
    local IteratePosImpl = function() -- This function calculates returns the cur, next position in a loop. If called without check, it loops infinitely

        local nextpos = {x = startpos.x, y = startpos.y}        
        local step = clockwise and {x = 0, y = -1} or { x = -1, y = 0 }

        return function()

            curpos = {x = nextpos.x, y = nextpos.y}
            nextpos.x = nextpos.x + step.x
            nextpos.y = nextpos.y + step.y
            if (((nextpos.x == loop or nextpos.x == col - loop + 1) and step.y == 0) or 
                ((nextpos.y == loop or nextpos.y == row - loop + 1) and step.x == 0)) then --Hit a corner in the loop

                local tempstep = {x = step.x, y = step.y}
                step.x = clockwise and tempstep.y or -tempstep.y
                step.y = clockwise and -tempstep.x or tempstep.x
                -- retract next step with new step
                nextpos.x = curpos.x + step.x 
                nextpos.y = curpos.y + step.y

            end         
            return curpos, nextpos
        end
    end
    local IteratePos = IteratePosImpl() -- make an instance
    local curpos, nextpos = IteratePos()
    while (true) do
        if(nextpos.x == startpos.x and nextpos.y == startpos.y) then            
            coroutine.yield(curpos)
            SpiralGen(loop+1) -- Go one step inner, since we're done with this loop
            break -- done with inner loop, get out
        else
            if(curpos.x < loop + 1 or curpos.x > col - loop or curpos.y < loop + 1 or curpos.y > row - loop) then
                break -- done with all elemnts, no place to loop further, break out of recursion
            else
                local curposL = {x = curpos.x, y = curpos.y}
                curpos, nextpos = IteratePos()
                coroutine.yield(curposL)
            end
        end     
    end 
end


local Spiral = function(rowP, colP, clockwiseP)
    row = rowP
    col = colP
    clockwise = clockwiseP
    return coroutine.wrap(function() SpiralGen(0) end) -- make a coroutine that returns all the values as an iterator
end


--test
for pos in Spiral(10,2,true) do
    print (pos.y, pos.x)
end

for pos in Spiral(10,9,false) do
    print (pos.y, pos.x)
end
Golda answered 6/9, 2013 at 17:35 Comment(0)
L
1

//PHP implementation

function spiral($n) {

    $r = intval((sqrt($n + 1) - 1) / 2) + 1;

    // compute radius : inverse arithmetic sum of 8+16+24+...=
    $p = (8 * $r * ($r - 1)) / 2;
    // compute total point on radius -1 : arithmetic sum of 8+16+24+...

    $en = $r * 2;
    // points by face

    $a = (1 + $n - $p) % ($r * 8);
    // compute de position and shift it so the first is (-r,-r) but (-r+1,-r)
    // so square can connect

    $pos = array(0, 0, $r);
    switch (intval($a / ($r * 2))) {
        // find the face : 0 top, 1 right, 2, bottom, 3 left
        case 0:
            $pos[0] = $a - $r;
            $pos[1] = -$r;
            break;
        case 1:
            $pos[0] = $r;
            $pos[1] = ($a % $en) - $r;
            break;
        case 2:
            $pos[0] = $r - ($a % $en);
            $pos[1] = $r;
            break;
        case 3:
            $pos[0] = -$r;
            $pos[1] = $r - ($a % $en);
            break;
    }
    return $pos;
}

for ($i = 0; $i < 168; $i++) {

    echo '<pre>';
    print_r(spiral($i));
    echo '</pre>';
}
Lierne answered 26/6, 2014 at 13:20 Comment(1)
This is a cool script but an oddity happens. When you fill a matrix with the positions generated, it will generate a matrix where the center is never filled: [x][x][x] [x][0][x] [x][x][x] Technically, the spiral should start at a point and then turn edge at each possible empty spot, thus, at the end there shouldn't be any [0] If drawing Ulam spirals with this formula, that is important. Anyone knows how to adjust this? The 4th position is the problem it should turn but proceeds bu one, then turns.Heavyweight
C
1

C# version, handles non-square sizes as well.

private static Point[] TraverseSpiral(int width, int height) {
    int numElements = width * height + 1;
    Point[] points = new Point[numElements];

    int x = 0;
    int y = 0;
    int dx = 1;
    int dy = 0;
    int xLimit = width - 0;
    int yLimit = height - 1;
    int counter = 0;

    int currentLength = 1;
    while (counter < numElements) {
        points[counter] = new Point(x, y);

        x += dx;
        y += dy;

        currentLength++;
        if (dx > 0) {
            if (currentLength >= xLimit) {
                dx = 0;
                dy = 1;
                xLimit--;
                currentLength = 0;
            }
        } else if (dy > 0) {
            if (currentLength >= yLimit) {
                dx = -1;
                dy = 0;
                yLimit--;
                currentLength = 0;
            }
        } else if (dx < 0) {
            if (currentLength >= xLimit) {
                dx = 0;
                dy = -1;
                xLimit--;
                currentLength = 0;
            }
        } else if (dy < 0) {
            if (currentLength >= yLimit) {
                dx = 1;
                dy = 0;
                yLimit--;
                currentLength = 0;
            }
        }

        counter++;
    }

    Array.Reverse(points);
    return points;
}
Churchlike answered 18/7, 2016 at 15:40 Comment(0)
D
1

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 length m = 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 by m-1 units, then repeating for the perpendicular 3 segments of m-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 by 3(m+1), then la [left ascending] by 2(m+1), and so on - hopefully this is self-explanatory.

import DataStructures: OrderedDict, merge

function spiral(loc::Int)
    s = sqrt(loc-1) |> floor |> Int
    if s % 2 == 0
        s -= 1
    end
    s = (s+1)/2 |> Int
    return s
end

function perimeter(n::Int)
    n > 0 || return OrderedDict([1,[0,0]])
    m = 2n + 1 # width/height of the spiral [square] indexed by n
    # loc_max = m^2
    # loc_min = (2n-1)^2 + 1
    ra = [[m^2-(y+3m-3), [n,n-y]] for y in (m-2):-1:0]
    la = [[m^2-(y+2m-2), [y-n,n]] for y in (m-2):-1:0]
    ld = [[m^2-(y+m-1), [-n,y-n]] for y in (m-2):-1:0]
    rd = [[m^2-y, [n-y,-n]] for y in (m-2):-1:0]
    return OrderedDict(vcat(ra,la,ld,rd))
end

function walk(n)
    cds = OrderedDict(1 => [0,0])
    n > 0 || return cds
    for i in 1:n
        cds = merge(cds, perimeter(i))
    end
    return cds
end

So for your first example, plugging m = 3 into the equation to find n gives n = (5-1)/2 = 2, and walk(2) gives an ordered dictionary of locations to coordinates, which you can turn into just an array of coordinates by accessing the dictionary's vals field:

walk(2)
DataStructures.OrderedDict{Any,Any} with 25 entries:
  1  => [0,0]
  2  => [1,0]
  3  => [1,1]
  4  => [0,1]
  ⋮  => ⋮

[(co[1],co[2]) for co in walk(2).vals]
25-element Array{Tuple{Int64,Int64},1}:
 (0,0)  
 (1,0)  
 ⋮       
 (1,-2) 
 (2,-2)

Note that for some functions [e.g. norm] it can be preferable to leave the coordinates in arrays rather than Tuple{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 by y (here for x=5,y=3) after calculating the full spiral then intersect this matrix against the values from walk.

grid = [[x,y] for x in -2:2, y in -1:1]
5×3 Array{Array{Int64,1},2}:
 [-2,-1]  [-2,0]  [-2,1]
   ⋮       ⋮       ⋮ 
 [2,-1]   [2,0]   [2,1]

[(co[1],co[2]) for co in intersect(walk(2).vals, grid)]
15-element Array{Tuple{Int64,Int64},1}:
 (0,0)  
 (1,0)  
 ⋮ 
 (-2,0) 
 (-2,-1)
Dentiform answered 6/12, 2017 at 16:53 Comment(0)
T
0

This is based on your own solution, but we can be smarter about finding the corners. This makes it easier to see how you might skip over the areas outside if M and N are very different.

def spiral(X, Y):
    x = y = 0
    dx = 0
    dy = -1
    s=0
    ds=2
    for i in range(max(X, Y)**2):
            if abs(x) <= X and abs(y) <= Y/2:
                    print (x, y)
                    # DO STUFF...
            if i==s:
                    dx, dy = -dy, dx
                    s, ds = s+ds/2, ds+1
            x, y = x+dx, y+dy

and a generator based solution that is better than O(max(n,m)^2), It is O(nm+abs(n-m)^2) because it skips whole strips if they are not part of the solution.

def spiral(X,Y):
X = X+1>>1
Y = Y+1>>1
x = y = 0
d = side = 1
while x<X or y<Y:
    if abs(y)<Y:
        for x in range(x, x+side, d):
            if abs(x)<X: yield x,y
        x += d
    else:
        x += side
    if abs(x)<X:
        for y in range(y, y+side, d):
            if abs(y)<Y: yield x,y
        y += d
    else:
        y += side
    d =-d
    side = d-side
Testudo answered 12/10, 2009 at 22:17 Comment(0)
F
0
Here is my attempt for simple C solution. First print the outer spiral and move one block inside..and repeat.

#define ROWS        5
#define COLS        5
//int A[ROWS][COLS] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {11, 12, 13, 14}, {15, 16, 17, 18} };
//int A[ROWS][COLS] = { {1, 2, 3}, {6, 7, 8}, { 12, 13, 14} };
//int A[ROWS][COLS] = { {1, 2}, {3, 4}};

int A[ROWS][COLS] = { {1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}, {11, 12, 13, 14, 15} , {16, 17, 18, 19, 20}, {21, 22, 23, 24, 25} };


void print_spiral(int rows, int cols)
{
    int row = 0;
    int offset = 0;

    while (offset < (ROWS - 1)) {
        /* print one outer loop at a time. */
        for (int col = offset; col <= cols; col++) {
            printf("%d ", A[offset][col]);
        }

        for (row = offset + 1; row <= rows; row++) {
            printf("%d ", A[row][cols]);
        }

        for (int col = cols - 1; col >= offset; col--) {
            printf("%d ", A[rows][col]);
        }

        for (row = rows - 1; row >= offset + 1; row--) {
            printf("%d ", A[row][offset]);
        }

       /* Move one block inside */
        offset++;
        rows--;
        cols--;
    }
    printf("\n");
}

int _tmain(int argc, _TCHAR* argv[])
{
    print_spiral(ROWS-1, COLS-1);
    return 0;
}
Forty answered 15/3, 2013 at 4:5 Comment(0)
A
0

This is my very very bad solution, made from bare minimum knowledge of Java. Here I have to place units on a field in a spiral. Units cannot be placed on top of other units or on mountains or in the ocean.

To be clear. This is not a good solution. This is a very bad solution added for the fun of other people to laugh at how bad it can be done

private void unitPlacementAlgorithm(Position p, Unit u){
    int i = p.getRow();
    int j = p.getColumn();

    int iCounter = 1;
    int jCounter = 0;

    if (getUnitAt(p) == null) {
            unitMap.put(p, u);
    } else {
        iWhileLoop(i, j, iCounter, jCounter, -1, u);
    }

}

private void iWhileLoop(int i, int j, int iCounter, int jCounter, int fortegn, Unit u){
    if(iCounter == 3) {
        for(int k = 0; k < 3; k++) {
            if(k == 2) { //This was added to make the looping stop after 9 units
                System.out.println("There is no more room around the city");
                return; 
            }
            i--;

            if (getUnitAt(new Position(i, j)) == null 
                && !(getTileAt(new Position(i, j)).getTypeString().equals(GameConstants.OCEANS)) 
                && !(getTileAt(new Position(i, j)).getTypeString().equals(GameConstants.MOUNTAINS))) {
                    unitMap.put(new Position(i, j), u);
                    return;
            }
            iCounter--;
        }
    }

    while (iCounter > 0) {
        if (fortegn > 0) {
            i++;
        } else {
            i--;
        }

        if (getUnitAt(new Position(i, j)) == null 
            && !(getTileAt(new Position(i, j)).getTypeString().equals(GameConstants.OCEANS)) 
            && !(getTileAt(new Position(i, j)).getTypeString().equals(GameConstants.MOUNTAINS))) {
                unitMap.put(new Position(i, j), u);
                return;
        }
        iCounter--;
        jCounter++;
    }
    fortegn *= -1;
    jWhileLoop(i, j, iCounter, jCounter, fortegn, u);
}

private void jWhileLoop(int i, int j, int iCounter, int jCounter,
        int fortegn, Unit u) {
    while (jCounter > 0) {
        if (fortegn > 0) {
            j++;
        } else {
            j--;
        }

        if (getUnitAt(new Position(i, j)) == null 
            && !(getTileAt(new Position(i, j)).getTypeString().equals(GameConstants.OCEANS)) 
            && !(getTileAt(new Position(i, j)).getTypeString().equals(GameConstants.MOUNTAINS))) {
                unitMap.put(new Position(i, j), u);
                return;

        }
        jCounter--;
        iCounter++;
        if (jCounter == 0) {
            iCounter++;
        }

    }
    iWhileLoop(i, j, iCounter, jCounter, fortegn, u);
}

Cudos to anyone who can actually read this

Bonus question: What is the running time of this "algorithm"? :P

Arjuna answered 4/4, 2013 at 11:13 Comment(1)
+1 because of "This is a very bad solution added for the fun of other people to laugh at how bad it can be done".Jinni
A
0

Solution for AutoIt

#include <Math.au3>
#include <Array.au3>

Func SpiralSearch($xMax,$yMax)
    $x = 0
    $y = 0
    $dx = 0
    $dy = -1
    for $i=0 To _max($xMax, $yMax)^2-1 Step 1
        if -$xMax/2 < $x and $x <= $xMax/2 And -$yMax/2 < $y And $y <= $yMax/2 Then
            MsgBox(0, "We are here ", $x & " " & $y)
        EndIf
        if $x == $y or ($x < 0 and $x == -$y) or ($x > 0 and $x == 1-$y) Then
            _ArraySwap ($dx, $dy)
            $dx=-$dx
        EndIf
        $x += $dx
        $y += $dy
    Next
EndFunc
Angular answered 29/12, 2013 at 19:16 Comment(0)
T
0

I recently had a similar challenge where I had to create a 2D array and use a spiral matrix algorithm to sort and print the results. This C# code will work with a N,N 2D array. It is verbose for clarity and can likely be re-factored to fit your needs.

//CREATE A NEW MATRIX OF SIZE 4 ROWS BY 4 COLUMNS - SCALE MATRIX SIZE HERE
SpiralMatrix SM = new SpiralMatrix(4, 4);
string myData = SM.Read();


public class SpiralMatrix
{
    //LETS BUILD A NEW MATRIX EVERY TIME WE INSTANTIATE OUR CLASS
    public SpiralMatrix(int Rows, int Cols)
    {
        Matrix = new String[Rows, Cols];

        int pos = 1;
        for(int r = 0; r<Rows; r++){
            for (int c = 0; c < Cols; c++)
            {
                //POPULATE THE MATRIX WITH THE CORRECT ROW,COL COORDINATE
                Matrix[r, c] = pos.ToString();
                pos++;
            }
        }
    }

    //READ MATRIX
    public string Read()
    {
        int Row = 0;
        int Col = 0;

        string S = "";
        bool isDone = false;

        //CHECK tO SEE IF POSITION ZERO IS AVAILABLE
        if(PosAvailable(Row, Col)){
            S = ConsumePos(Row, Col);
        }


        //START READING SPIRAL
        //THIS BLOCK READS A FULL CYCLE OF RIGHT,DOWN,LEFT,UP EVERY ITERATION
        while(!isDone)
        {
            bool goNext = false;

            //READ ALL RIGHT SPACES ON THIS PATH PROGRESSION
            while (PosAvailable(Row, Col+1))
            {
                //Is ReadRight Avail
                Col++;
                S += ConsumePos(Row, Col);
                goNext = true;
            }

            //READ ALL DOWN SPACES ON THIS PATH PROGRESSION
            while(PosAvailable(Row+1, Col)){
                //Is ReadDown Avail
                Row++;
                S += ConsumePos(Row, Col);
                goNext = true;
            }

            //READ ALL LEFT SPACES ON THIS PATH PROGRESSION
            while(PosAvailable(Row, Col-1)){
                //Is ReadLeft Avail
                Col--;
                S += ConsumePos(Row, Col);
                goNext = true;
            }

            //READ ALL UP SPACES ON THIS PATH PROGRESSION
            while(PosAvailable(Row-1, Col)){
                //Is ReadUp Avail
                Row--;
                S += ConsumePos(Row, Col);
                goNext = true;
            }

            if(!goNext){
                //DONE - SET EXIT LOOP FLAG
                isDone = true;
            }
        }

        return S;
    }

    //DETERMINE IF THE POSITION IS AVAILABLE
    public bool PosAvailable(int Row, int Col)
    {
        //MAKE SURE WE ARE WITHIN THE BOUNDS OF THE ARRAY
        if (Row < Matrix.GetLength(0) && Row >= 0
            && Col < Matrix.GetLength(1) && Col >= 0)
        {
            //CHECK COORDINATE VALUE
            if (Matrix[Row, Col] != ConsumeChar)
                return true;
            else
                return false;
        }
        else
        {
            //WE ARE OUT OF BOUNDS
            return false;
        }
    }

    public string ConsumePos(int Row, int Col)
    {
        string n = Matrix[Row, Col];
        Matrix[Row, Col] = ConsumeChar;
        return n;
    }

    public string ConsumeChar = "X";
    public string[,] Matrix;
}
Themis answered 26/6, 2014 at 4:36 Comment(0)
A
0

I made this one with a friend that adjusts the spiral to the canvas aspect ratio on Javascript. Best solution I got for a image evolution pixel by pixel, filling the entire image.

Hope it helps some one.

var width = 150;
var height = 50;

var x = -(width - height)/2;
var y = 0;
var dx = 1;
var dy = 0;
var x_limit = (width - height)/2;
var y_limit = 0;
var counter = 0;

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');

setInterval(function(){
   if ((-width/2 < x && x <= width/2)  && (-height/2 < y && y <= height/2)) {
       console.log("[ " + x + " , " +  y + " ]");
       ctx.fillStyle = "#FF0000";
       ctx.fillRect(width/2 + x, height/2 - y,1,1);
   }
   if( dx > 0 ){//Dir right
       if(x > x_limit){
           dx = 0;
           dy = 1;
       }
   }
   else if( dy > 0 ){ //Dir up
       if(y > y_limit){
           dx = -1;
           dy = 0;
       }
   }
   else if(dx < 0){ //Dir left
       if(x < (-1 * x_limit)){
           dx = 0;
           dy = -1;
       }
   }
   else if(dy < 0) { //Dir down
       if(y < (-1 * y_limit)){
           dx = 1;
           dy = 0;
           x_limit += 1;
           y_limit += 1;
       }
   }
   counter += 1;
   //alert (counter);
   x += dx;
   y += dy;      
}, 1);

You can see it working on http://jsfiddle.net/hitbyatruck/c4Kd6/ . Just be sure to change the width and height of the canvas on the javascript vars and on the attributes on the HTML.

Anticosti answered 10/9, 2014 at 0:41 Comment(0)
V
0

Just for fun in Javascript:

function spiral(x, y) {
  var iy = ix = 0
    , hr = (x - 1) / 2
    , vr = (y - 1) / 2
    , tt = x * y
    , matrix = []
    , step = 1
    , dx = 1
    , dy = 0;

  while(matrix.length < tt) {

    if((ix <= hr && ix >= (hr * -1)) && (iy <= vr && (iy >= (vr * -1)))) {
      console.log(ix, iy);
      matrix.push([ix, iy]);
    }

    ix += dx;
    iy += dy;

    // check direction
    if(dx !== 0) {
      // increase step
      if(ix === step && iy === (step * -1)) step++;

      // horizontal range reached
      if(ix === step || (ix === step * -1)) {
        dy = (ix === iy)? (dx * -1) : dx;
        dx = 0;  
      }
    } else {
      // vertical range reached
      if(iy === step || (iy === step * -1)) {
        dx = (ix === iy)? (dy * -1) : dy;
        dy = 0;
      }
    }
  }

  return matrix;
}

var sp = spiral(5, 3);
Viscid answered 21/10, 2015 at 2:0 Comment(0)
W
0

I am sharing this code which I designed for a different purpose; it is about finding the Column number "X", and the row number "Y" of array element @ spiral index "index". This function takes the width "w" and height "h" of the matrix, and the required "index". Of course, this function can be used to produce the same required output. I think it is the fastest possible method (as it jumps over cells instead of scanning them).

    rec BuildSpiralIndex(long w, long h, long index = -1)
    {  
        long count = 0 , x = -1,  y = -1, dir = 1, phase=0, pos = 0,                            length = 0, totallength = 0;
        bool isVertical = false;
        if(index>=(w*h)) return null;

        do 
        {                
            isVertical = (count % 2) != 0;
            length = (isVertical ? h : w) - count/2 - count%2 ;
            totallength += length;
            count++;
        } while(totallength<index);

        count--; w--; h--;
        phase = (count / 4); pos = (count%4);
        x = (pos > 1 ? phase : w - phase);
        y = ((pos == 1 || pos == 2) ? h - phase : phase) + (1 * (pos == 3 ? 1 : 0));
        dir = pos > 1 ? -1 : 1;
        if (isVertical) y -= (totallength - index - 1) * dir;
        else x -= (totallength - index -1) * dir;
        return new rec { X = x, Y = y };
    }
Wooden answered 17/8, 2016 at 0:36 Comment(0)
W
0

Python looping clockwise spiral code using Can Berk Güder answer.

def spiral(X, Y):
    x = y = 0
    dx = 0
    dy = 1
    for i in range(max(X, Y)**2):
        if (-X/2 < x <= X/2) and (-Y/2 < y <= Y/2):
            print (x, y)
            # DO STUFF...
        if x == -y or (x < 0 and x == y) or (x > 0 and x-1 == y):
            dx, dy = dy, -dx
        x, y = x+dx, y+dy
Walston answered 26/9, 2016 at 17:32 Comment(1)
It's clockwise 🔃 and I cited Can Berk Güder. Original question is for counter clockwise 🔄. I needed a clockwise function so I felt it would be useful to leave it there.Walston
S
0

Davidont's excellent solution in VB.Net

    Public Function Spiral(n As Integer) As RowCol
    ' given n an index in the squared spiral
    ' p the sum of point in inner square
    ' a the position on the current square
    ' n = p + a
    ' starts with row 0 col -1
    Dim r As Integer = CInt(Math.Floor((Math.Sqrt(n + 1) - 1) / 2) + 1)

    ' compute radius : inverse arithmetic sum of 8+16+24+...=
    Dim p As Integer = (8 * r * (r - 1)) \ 2
    ' compute total point on radius -1 : arithmetic sum of 8+16+24+...

    Dim en As Integer = r * 2
    ' points by face

    Dim a As Integer = (1 + n - p) Mod (r * 8)
    ' compute the position and shift it so the first is (-r,-r) but (-r+1,-r)
    ' so square can connect

    Dim row As Integer
    Dim col As Integer

    Select Case Math.Floor(a \ (r * 2))
        ' find the face : 0 top, 1 right, 2, bottom, 3 left
        Case 0
            row = a - r
            col = -r
        Case 1
            row = r
            col = (a Mod en) - r
        Case 2
            row = r - (a Mod en)
            col = r
        Case 3
            row = -r
            col = r - (a Mod en)
    End Select

    Return New RowCol(row, col)
End Function
Slade answered 9/11, 2017 at 11:42 Comment(0)
F
0

This is my approach for a square spiral in c#, i made this some time ago, i just thought i could add it, since it's different from all the others, not the best, but just a different way, i am sure it can be adapted for a non-square too.

This approach i take the max number of steps in, instead of the max vector tho.

The main thing about this approach is the corners, there's some adjustments for the first step and the "progress" step needed to go out of the "corner" in the right bottom corner.

private void Spiral(int sequence)
{
    const int x = 0;
    const int y = 1;
    int[,] matrix = new int[2, sequence];
    int dirX, dirY, prevX, prevY, curr;
    dirX = dirY = prevX = prevY = curr = default(int);

    do
    {
        if (curr > 0)
        {
            prevX = matrix[x, curr - 1];
            prevY = matrix[y, curr - 1];
        }

        //Change direction based on the corner.
        if (Math.Abs(prevX) == Math.Abs(prevY) && curr > 0)
        {
            dirX = dirY = 0;

            if (prevY > 0 && prevX > 0)
                dirX = -1;
            else if (prevY > 0 && prevX < 0)
                dirY = -1;
            else if (prevY < 0 && prevX < 0)
                dirX = 1;
            else if (prevY < 0 && prevX > 0) //Move forward
                dirX = 1;
            else if (prevY == 0 && prevX == 0) //For the first step.
                dirX = 1;
        }
        else if (prevY < 0 && prevX > 0 && (Math.Abs(matrix[x, curr - 2]) == Math.Abs(matrix[y, curr - 2]))) //Move forward
        {
            dirX = 0;
            dirY = 1;
        }
        else if (prevX == 1 && prevY == 0) //For the second step.
        {
            dirY = 1;
            dirX = 0;
        }

        matrix[x, curr] = prevX + dirX;
        matrix[y, curr] = prevY + dirY;

        System.Console.Write($"({matrix[x, curr]},{matrix[y, curr]}) ");

    } while (++curr < sequence);
}
Frizzy answered 29/1, 2018 at 15:57 Comment(0)
D
0

This is a Python/numpy solution which fills any rectangle with spiral. It solves slightly different problem than the original question but that is what I needed.

import numpy as np
import matplotlib.pyplot as plt

def spiral(m, n):
    M = np.zeros([m, n], dtype=int)
    i, j = 0, 0 # location of "turtle"
    di, dj = 0, 1 # direction of movement
    h = (np.min([m,n]))/2
    for ii in range(m * n):
        M[i, j] = ii
        if (i < h and (i == j+1 or i+1 == n-j)) or (i >= m-h and (m-i == n-j or m-i == j+1)):
            di, dj = dj, -di # turn clockwise
        i, j = i + di, j + dj
    return M

plt.imshow(spiral(16, 24))

spiral

Dugout answered 17/5, 2019 at 11:18 Comment(0)
C
0

A Kotlin spiral.

data class Point(val x: Int, val y: Int) {
    operator fun plus(p: Point): Point = Point(x + p.x, y + p.y)

    override fun toString() = "($x, $y)"

    companion object {
        enum class Directions(val d: Point) {
            RIGHT(Point(1, 0)),
            UP(Point(0, 1)),
            LEFT(Point(-1, 0)),
            DOWN(Point(0, -1))
        }

        fun spiral() = sequence {
            var p = Point(0, 0)
            // Always start at the origin.
            yield(p)
            // 0, 2, 4, 6 ...
            generateSequence(0) { it + 2 }.forEach { n ->
                // For each of the 4 directions
                Directions.values().forEach { d ->
                    // actual length depends slightly on direction
                    val l = n + when (d) {
                        Directions.RIGHT, Directions.UP -> 1
                        Directions.LEFT, Directions.DOWN -> 2
                    }
                    // run to the next corner
                    for (i in 1..l) {
                        p += d.d
                        yield(p)
                    }
                }
            }
        }
    }
}
Corm answered 6/9, 2020 at 7:56 Comment(0)
H
0

I couldn't find a Rust implementation, so I picked the one from @neatsu as inspiration and wrote it in Rust.

My implementation "fills" the area described by the size variable, i.e. it stops as soon as the next step would go outside of the given dimensions, and starts at the origin location.

I also changed the direction to go Right -> Up -> Left -> Down.

use Direction::*;
enum Direction { Up, Down, Left, Right }
impl Direction {
    fn next(&mut self) {
        *self = match self { Up => Left, Down => Right, Left => Down, Right => Up }
    }
}
fn spiral(origin: (f64, f64), size: (f64, f64), step: f64) -> Vec<(f64, f64)> {
    let (mut distance, mut range, mut direction, mut x, mut y) = (0,1,Right, origin.0, origin.1);
    let mut res_vec = Vec::<(f64, f64)>::new();
    while x.abs() <= size.0/2.0 && y.abs() <= size.1/2.0 {
        res_vec.push((x, y)); distance += 1;
        match direction {
            Up => { y += step; if distance >= range { direction.next(); distance = 0; range += 1;}},
            Down => { y -= step; if distance >= range { direction.next(); distance = 0; range += 1; }},
            Left => { x -= step; if distance >= range { direction.next(); distance = 0; }},
            Right => { x += step; if distance >= range { direction.next(); distance = 0; }},
        }
    }
    return res_vec;
}

Usage:

let res = spiral((0.0, 0.0), (2.0, 2.0), 0.2);
print!("{:?}", res);
Hsinking answered 28/9, 2023 at 17:48 Comment(0)
S
-1

I really like this challenge 1+ for this post. I tried this by Ruby Code :

For 3X3 Square matrix

(0..8).each do |i|
    j = Math.sqrt(i).round
    k = (j ** 2 - i).abs - j
    p = [k, -k].map {|l| (l + j ** 2 - i - (j % 2)) * 0.5 * (-1) ** j}.map(&:to_i)
    puts "(#{p[0]}, #{p[1]}) "
end

Output:

(0, 0) (1, 0) (1, 1) (0, 1) (-1, 1) (-1, 0) (-1, -1) (0, -1) (1, -1) 

For 5X3 as you mentioned in image

iter = (0..19).to_enum
while true
    i = iter.next
    j = Math.sqrt(i).round
    k = (j ** 2 - i).abs - j
    p = [k, -k].map {|l| (l + j ** 2 - i - (j % 2)) * 0.5 * (-1) ** j}.map(&:to_i)
    print "(#{p[0]}, #{p[1]}) "
  if i == 11
    5.times {i = iter.next}
  end
end

Output for this:

(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)
Skinner answered 19/9, 2014 at 13:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.