Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Looping in a spiral outside-in

I'm looking to loop through a matrix similar to Looping in a spiral but looping outside-in, instead of inside-out. Can anyone help me with a good way to do this for a matrix of any size, ideally in Ruby?

Example: In a 3x4 matrix I want to start at [0,0] going right, then move down once I reach [3,0], move left at [3,2] etc.

[0,0] [1,0] [2,0] [3,0]
[0,1] [1,1] [2,1] [3,1]
[0,2] [1,2] [2,2] [3,2]

The order to move is shown below:

0  1  2  3
9  10 11 4
8  7  6  5

And the output would be:

[0,0], [1,0], [2,0], [3,0], [3,1], [3,2], [2,2], [1,2], [0,2], [0,1], [1,1], [2,1]
like image 297
ant Avatar asked Mar 03 '15 16:03

ant


4 Answers

Without loss of generality, let's write the array as:

arr = [
  [ 1,  2,  3, 4,],
  [12, 13, 14, 5,],
  [11, 16, 15, 6,],
  [10,  9,  8, 7,]
]

The desired result is:

[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]

I will use a helper:

def rotate_anticlockwise(arr)
  arr.map(&:reverse).transpose
end

For example:

rotate_anticlockwise(arr)
  #=> [[4,  5,  6, 7],
  #    [3, 14, 15, 8],
  #    [2, 13, 16, 9],
  #    [1, 12, 11, 10]] 

We can now compute the desired result as follows:

out = []
a = arr.map(&:dup)
while a.any?
  out.concat(a.shift)
  a = rotate_anticlockwise(a)
end
out
  # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] 
like image 105
Cary Swoveland Avatar answered Nov 15 '22 20:11

Cary Swoveland


This is a problem that has always fascinated me. @CarySwoveland has the trick that makes this really elegant in Ruby. Here it is in a one-line method:

def spiral(matrix)
  matrix.empty? ? [] : matrix.shift + spiral(matrix.transpose.reverse)
end

arr = [[ 1,  2,  3, 4,],
       [12, 13, 14, 5,],
       [11, 16, 15, 6,],
       [10,  9,  8, 7,]]

spiral(arr)

# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]

One flaw of this approach, however, is that it mutates the original matrix arr:

arr

# => [[12, 13, 14, 5], [11, 16, 15, 6], [10, 9, 8, 7]]

There are several answers in this gist that are also worth a look.

like image 42
O-I Avatar answered Nov 15 '22 19:11

O-I


Here is working C code that prints a 2D matrix in clockwise fashion from outside to inside.

int n = 2, m = 5;
    int arr[2][5] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    int top = 0, bottom = n-1, right = m-1, left = 0, i=0, j=0;

    printf("%d ", arr[i][j]);

    while(1){
        if(top>bottom || left>right) break;


        if(bottom==top){
            for(j=left; j<=right; j++)
                printf("%d ", arr[top][j]);
            break;
        }

        if(right==left){
            for(i=top; i<=bottom; i++)
                printf("%d ", arr[left][i]);
            break;
        }

        int first = 1;
        while(j<=right){
            if(first){
                j++;
                first = 0;
                continue;
            }

            printf("%d ", arr[i][j]);
            j++;
        }
        j--; right--;

        first = 1;
        while(i<=bottom){
            if(first){
                i++;
                first = 0;
                continue;
            }           

            printf("%d ", arr[i][j]);
            i++;
        }
        i--; bottom--;

        first = 1;
        while(j>=left){
            if(first){
                j--;
                first = 0;
                continue;
            }

            printf("%d ", arr[i][j]);
            j--;
        }
        j++; left++;

        first = 1;
        while(i>=top+1){
            if(first){
                i--;
                first = 0;
                continue;
            }

            printf("%d ", arr[i][j]);
            i--;
        }
        i++; top++;
    }

The logic and reasoning behind the code is that you maintain the boundaries of the matrix that hasn't been printed till now. So at the start of the procedure the top boundary=0, bottom=n-1, left=0, right=n-1.

At every iteration in the outer while loop you check if the remaining matrix defined by the boundaries degenerates into a row or column matrix. Or if all the elements of the matrix have been printed following which it breaks out of the loop.

Moreover, the 'first' variable in each inner while loop keeps track whether the value we are printing is the first value for that row/col. The first value won't be printed because it would have already been printed by the loop just before it.

Time complexity : O(n)
Space complexity : O(1)

where n is the number of elements in the array.

like image 2
Nikunj Banka Avatar answered Nov 15 '22 19:11

Nikunj Banka


This method does what you need, it loops through the outer spiral and then recursively calls itself to loop through the inner spirals

def inward_spiral(height, width, current_height, current_width)
  if height <= 0 || width <= 0
    return
  end

  # Right
  (width-1).times do
      puts "[#{current_width}, #{current_height}]"
      current_width += 1 
  end

  # Down
  (height-1).times do
    puts "[#{current_width}, #{current_height}]"
    current_height += 1
  end

  # Left
  if height > 1
      (width-1).times do 
          puts "[#{current_width}, #{current_height}]"
          current_width -= 1 
      end
  end

  # Up
  if width > 1
      (height-2).times do
          puts "[#{current_width}, #{current_height}]"
          current_height -= 1
      end
  end

  puts "[#{current_width}, #{current_height}]"
  inward_spiral(height-2, width-2, current_width + 1, current_height)
end

And then call it inward_spiral(3,4,0,0)

You can also fill up a matrix to draw the spiral if at each step you fill it up with the direction you're going to get something like this:

→  →  →  →  ↴ 
↱  →  →  ↴  ↓ 
↑  ↱  ↴  ↓  ↓ 
↑  ↑  ↓  ↓  ↓ 
↑  ↑  ↓  ↓  ↓ 
↑  ↑  ↓  ↓  ↓ 
↑  ↑  ↓  ↓  ↓ 
↑  ↑  ✓  ↓  ↓ 
↑  ↑  ←  ⤶  ↓ 
↑  ←  ←  ←  ⤶ 
like image 2
hbejgel Avatar answered Nov 15 '22 19:11

hbejgel