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]
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]
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.
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.
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:
→ → → → ↴
↱ → → ↴ ↓
↑ ↱ ↴ ↓ ↓
↑ ↑ ↓ ↓ ↓
↑ ↑ ↓ ↓ ↓
↑ ↑ ↓ ↓ ↓
↑ ↑ ↓ ↓ ↓
↑ ↑ ✓ ↓ ↓
↑ ↑ ← ⤶ ↓
↑ ← ← ← ⤶
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With