代码
def nxt(rows, cols, row, col)
case row
when rows[:first]
col == cols[:last] ? [row+1, col] : [row, col+1]
when rows[:last]
col == cols[:first] ? [row-1, col] : [row, col-1]
else
col == cols[:last] ? [row+1, col] : [row-1, col]
end
end
def rotate_array_times(matrix, n)
arr = matrix.dup.map(&:dup)
nrows, ncols = arr.size, arr.first.size
0.upto([nrows, ncols].min/2-1) do |m|
rows = { first: m, last: nrows-m-1 }
cols = { first: m, last: ncols-m-1 }
rect_size = 2 * (nrows + ncols) - 8*m - 4
rotations = n % rect_size
row = col = rrow = rcol = m
rotations.times { rrow, rcol = nxt(rows, cols, rrow, rcol) }
rect_size.times do
arr[row][col] = matrix[rrow][rcol]
row, col = nxt(rows, cols, row, col)
rrow, rcol = nxt(rows, cols, rrow, rcol)
end
end
arr
end
例子
matrix = [
[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12],
[13, 14, 15, 16]
]
(1..3).each { |n| p rotate_array_times(matrix, n) }
[[2, 3, 4, 8],
[1, 7, 11, 12],
[5, 6, 10, 16],
[9, 13, 14, 15]]
[[3, 4, 8, 12],
[2, 11, 10, 16],
[1, 7, 6, 15],
[5, 9, 13, 14]]
[[4, 8, 12, 16],
[3, 10, 6, 15],
[2, 11, 7, 14],
[1, 5, 9, 13]]
matrix = (1..24).each_slice(4).to_a
(1..3).each { |n| p rotate_array_times(matrix, n) }
matrix = (1..48).each_slice(8).to_a
#=> [[ 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, 26, 27, 28, 29, 30, 31, 32],
# [33, 34, 35, 36, 37, 38, 39, 40],
# [41, 42, 43, 44, 45, 46, 47, 48]]
(1..3).each { |n| p rotate_array_times(matrix, n) }
[[ 2, 3, 4, 5, 6, 7, 8, 16],
[ 1, 11, 12, 13, 14, 15, 23, 24],
[ 9, 10, 20, 21, 22, 30, 31, 32],
[17, 18, 19, 27, 28, 29, 39, 40],
[25, 26, 34, 35, 36, 37, 38, 48],
[33, 41, 42, 43, 44, 45, 46, 47]]
[[ 3, 4, 5, 6, 7, 8, 16, 24],
[ 2, 12, 13, 14, 15, 23, 31, 32],
[ 1, 11, 21, 22, 30, 29, 39, 40],
[ 9, 10, 20, 19, 27, 28, 38, 48],
[17, 18, 26, 34, 35, 36, 37, 47],
[25, 33, 41, 42, 43, 44, 45, 46]]
[[ 4, 5, 6, 7, 8, 16, 24, 32],
[ 3, 13, 14, 15, 23, 31, 39, 40],
[ 2, 12, 22, 30, 29, 28, 38, 48],
[ 1, 11, 21, 20, 19, 27, 37, 47],
[ 9, 10, 18, 26, 34, 35, 36, 46],
[17, 25, 33, 41, 42, 43, 44, 45]]
说明
nxt
nxt(rows, cols, row, col)
给定行和列的索引row
和col
,返回替换索引[row,col]
处的元素(也在周长上)的“下一个”元素的索引[next_row,next_col]
。该子阵列由散列rows
和cols
给出,每个散列都具有键:first
和:last
。
我们考虑一个具有4个元素(行)的数组arr
,每个元素(行)具有6个值(列)。那么
nrows, ncols = arr.size, arr.first.size
如果
m = 0
。
rows = { first: m, last: nrows-m-1 }
cols = { first: m, last: ncols-m-1 }
可以看出,rows
和cols
描述了数组matrix
的“外围”。我们可以看到nxt
的工作原理如下。
first_row, first_col = rows[:first], cols[:first]
row, col = first_row, first_col
print "[#{row}, #{col}]"
loop do
next_row, next_col = nxt(rows, cols, row, col)
print "->[#{next_row}, #{next_col}]"
row, col = next_row, next_col
(puts; break) if [row, col] == [first_row, first_col]
end
[0, 0]->[0, 1]->[0, 2]->[0, 3]->[0, 4]->[0, 5]->[1, 5]->[2, 5]->[3, 5]->
[3, 4]->[3, 3]->[3, 2]->[3, 1]->[3, 0]->[2, 0]->[1, 0]->[0, 0]
如果
m = 1
,上述计算结果为:
[1, 1]->[1, 2]->[1, 3]->[1, 4]->[2, 4]->[2, 3]->[2, 2]->[2, 1]->[1, 1]
rotate_array_times
这个方法是将 matrix
和 arr
的元素按指定方式旋转 n
次并返回结果数组的深层拷贝。
为了加快计算速度,n
被替换为自身的模数。例如对于一个 4x4 的数组,在经过12次迭代后,数组周长将回到其原始值。因此,执行 n % 12
次旋转就足够了。
matrix
包含 n = [matrix.size, matrix.first.size].min
个子数组,它们的周长要进行旋转。每个子数组的左上角坐标为 [m,m]
,其中 m = 0..n-1
。
对于由 m
指定的子数组,第一步是确定 matrix
中要替换 arr
中 [m,m]
元素的位置。这在下面的代码行中完成:
rotations.times { rrow, rcol = nxt(rows, cols, rrow, rcol) }
("
"rrow"
和
"rcol"
分别代表“替换行”和“替换列”。此时,位于
arr
位置
row #=> m
,
col #=> m
的元素将被替换为
matrix
中由
rrow
和
rcol
给出位置的元素。然后,对于需要旋转的子数组周长中的每个元素,执行以下操作:)
arr[row][col] = matrix[rrow][rcol]
row, col = nxt(rows, cols, row, col)
rrow, rcol = nxt(rows, cols, rrow, rcol)
优化效率
通过替换以下这行代码,可以稍微提高效率:
rotations.times { rrow, rcol = nxt(rows, cols, rrow, rcol) }
使用
rrow, rcol = first_replacement_loc(rows, cols, rotations)
并添加以下方法。
def first_replacement_loc(rows, cols, rotations)
ncm1 = cols[:last]-cols[:first]
nrm1 = rows[:last]-rows[:first]
return [rows[:first], cols[:first]+rotations] if rotations <= ncm1
rotations -= ncm1
return [rows[:first]+rotations, cols[:last]] if rotations <= nrm1
rotations -= nrm1
return [rows[:last], cols[:last]-rotations] if rotations <= ncm1
rotations -= ncm1
[rows[:last]-rotations, cols[:first]]
end