Looping in a spiral

前端 未结 30 2418
独厮守ぢ
独厮守ぢ 2020-11-22 15:07

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 fe

相关标签:
30条回答
  • 2020-11-22 15:30

    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)
    
    0 讨论(0)
  • 2020-11-22 15:30

    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
    
    0 讨论(0)
  • 2020-11-22 15:30

    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;
    }
    
    0 讨论(0)
  • 2020-11-22 15:30

    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
    
    0 讨论(0)
  • 2020-11-22 15:31

    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;
        }
    }
    
    0 讨论(0)
  • 2020-11-22 15:31
    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.

    0 讨论(0)
提交回复
热议问题