Looping in a spiral

前端 未结 30 2414
独厮守ぢ
独厮守ぢ 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:12

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

    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.

    0 讨论(0)
  • 2020-11-22 15:17

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

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

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

    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

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