介绍性发言
这个问题与以螺旋顺序打印数组的问题密切相关。事实上,如果我们已经有一个函数可以做到这一点,那么问题就相对简单了。
上面有海量的资源如何产生螺旋矩阵 https://rosettacode.org/wiki/Spiral_matrix或如何loop https://stackoverflow.com/questions/398299/looping-in-a-spiral or print https://stackoverflow.com/questions/726756/print-two-dimensional-array-in-spiral-order螺旋顺序的数组。即便如此,我还是决定使用 numpy 数组编写自己的版本。这个想法不是原创的,但是使用 numpy 使代码更加简洁。
另一个原因是,我发现的大多数生成螺旋矩阵的示例(包括问题和其他答案中的代码)仅处理奇数 n 的大小为 n x n 的方阵。在其他大小的矩阵中找到起点(或终点)可能很棘手。例如,对于 3x5 矩阵,它不能是中间单元格。下面的代码是通用的,起始(结束)点的位置取决于函数的选择spiral_xxx
.
Code
第一个函数按顺时针螺旋顺序解包数组:
import numpy as np
def spiral_cw(A):
A = np.array(A)
out = []
while(A.size):
out.append(A[0]) # take first row
A = A[1:].T[::-1] # cut off first row and rotate counterclockwise
return np.concatenate(out)
我们可以用八种不同的方式编写这个函数,具体取决于我们从哪里开始以及如何旋转矩阵。我将给出另一个,它与问题中图像中的矩阵变换一致(稍后会很明显)。因此,进一步,我将使用这个版本:
def spiral_ccw(A):
A = np.array(A)
out = []
while(A.size):
out.append(A[0][::-1]) # first row reversed
A = A[1:][::-1].T # cut off first row and rotate clockwise
return np.concatenate(out)
怎么运行的:
A = np.arange(15).reshape(3,5)
print(A)
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]]
print(spiral_ccw(A))
[ 4 3 2 1 0 5 10 11 12 13 14 9 8 7 6]
请注意,结束(或开始)点不是中间单元格。该函数适用于所有类型的矩阵,但我们需要一个辅助函数来生成螺旋指数:
def base_spiral(nrow, ncol):
return spiral_ccw(np.arange(nrow*ncol).reshape(nrow,ncol))[::-1]
例如:
print(base_spiral(3,5))
[ 6 7 8 9 14 13 12 11 10 5 0 1 2 3 4]
现在两个人来了主要功能。一种将矩阵变换为相同维度的螺旋形式,另一种则恢复变换:
def to_spiral(A):
A = np.array(A)
B = np.empty_like(A)
B.flat[base_spiral(*A.shape)] = A.flat
return B
def from_spiral(A):
A = np.array(A)
return A.flat[base_spiral(*A.shape)].reshape(A.shape)
Examples
矩阵 3 x 5:
A = np.arange(15).reshape(3,5)
print(A)
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]]
print(to_spiral(A))
[[10 11 12 13 14]
[ 9 0 1 2 3]
[ 8 7 6 5 4]]
print(from_spiral(to_spiral(A)))
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]]
问题的矩阵:
B = np.arange(1,26).reshape(5,5)
print(B)
[[ 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]]
print(to_spiral(B))
[[21 22 23 24 25]
[20 7 8 9 10]
[19 6 1 2 11]
[18 5 4 3 12]
[17 16 15 14 13]]
print(from_spiral(to_spiral(B)))
[[ 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]]
Remark
如果您只打算使用固定大小的矩阵,例如 5x5,那么值得替换base_spiral(*A.shape)
在具有固定索引矩阵的函数的定义中,例如Ind
(where Ind = base_spiral(5,5)
).