我有一个列表列表(我在这里称之为矩阵),我希望在原地顺时针旋转90°(即没有应对另一个矩阵)。我想出了以下代码。
我对算法本身感兴趣,在纯python中(没有使用numpy左右转置)。
def rotate(M):
n = len(M)
m = len(M[0])
for i in range(n//2):
for j in range(i, m-1-i):
ii, jj = i, j
v0 = M[ii][jj]
for _ in range(3):
M[ii][jj] = M[n-1-jj][ii]
ii, jj = n-1-jj, ii
M[ii][jj] = v0
return M
虽然行数等于列数,但效果很好。有关修改我的函数以处理行数和列数不相等的情况的想法吗?
以下是一个例子:
input: [[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]]
output [[21, 16, 11, 6, 1],
[22, 17, 12, 7, 2],
[23, 18, 13, 8, 3],
[24, 19, 14, 9, 4],
[25, 20, 15, 10, 5]]
答案 0 :(得分:2)
始终使用numpy
进行矩阵运算。我假设m*n
numpy数组arr
。我首先使用np.transpose
函数进行转置,然后使用np.fliplr
函数将其翻转。
output = np.fliplr(np.transpose(arr))
如评论中所述,没有矩形矩阵的临时变量就无法进行就地替换。使用像这样的函数
来模拟行为(如果存储是你的问题)会更好def clockwise(matrix, row, column):
return matrix[-column][row]
答案 1 :(得分:2)
其他答案正确地建议rot90
或transpose
和fliplr
。如果您深入研究他们的代码,您会发现该操作可以缩减为transpose
和反向索引:
In [467]: arr=np.arange(1,26).reshape(5,5)
In [468]: arr.transpose()
Out[468]:
array([[ 1, 6, 11, 16, 21],
[ 2, 7, 12, 17, 22],
[ 3, 8, 13, 18, 23],
[ 4, 9, 14, 19, 24],
[ 5, 10, 15, 20, 25]])
In [469]: arr.transpose()[:,::-1]
Out[469]:
array([[21, 16, 11, 6, 1],
[22, 17, 12, 7, 2],
[23, 18, 13, 8, 3],
[24, 19, 14, 9, 4],
[25, 20, 15, 10, 5]])
单独transpose
和[:,::-1]
生成观看次数。也就是说,数组是新的,但它与原始数据共享一个数据缓冲区。但是,numpy
必须一起复制。换句话说,您无法从[21, 16, 11,...]
获取数字[1,2,3,...]
而无需重新排序。
transpose
和[::-1]
索引都是在编译代码中实现的。 transpose
实际上是一个肤浅的'操作,更改数组shape
和strides
(以及order
),但不重新排列任何值。单独[:,::-1]
进行strides
更改,但order
更改时,还必须执行数组副本。
In [470]: arr.__array_interface__
Out[470]:
{'data': (151576248, False),
'descr': [('', '<i4')],
'shape': (5, 5),
'strides': None,
'typestr': '<i4',
'version': 3}
In [471]: arr1 = arr.transpose()
In [472]: arr1.__array_interface__
Out[472]:
{'data': (151576248, False),
'descr': [('', '<i4')],
'shape': (5, 5),
'strides': (4, 20),
'typestr': '<i4',
'version': 3}
In [473]: arr2 = arr1.copy()
In [474]: arr2.__array_interface__
Out[474]:
{'data': (154237272, False),
'descr': [('', '<i4')],
'shape': (5, 5),
'strides': None,
'typestr': '<i4',
'version': 3}
In [475]: arr3 = arr2[:,::-1]
In [476]: arr3.__array_interface__
Out[476]:
{'data': (154237288, False),
'descr': [('', '<i4')],
'shape': (5, 5),
'strides': (20, -4), # or (4,-20) without the explicit copy()
'typestr': '<i4',
'version': 3}
这是一个pure Python
列表实现。 zip(*)
是转置的列表版本。 [::-1]
就像数组那样反转列表。
In [479]: alist1=arr.tolist()
In [480]: alist1
Out[480]:
[[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]]
In [481]: alist2=list(zip(*alist1))
In [482]: alist2
Out[482]:
[(1, 6, 11, 16, 21),
(2, 7, 12, 17, 22),
(3, 8, 13, 18, 23),
(4, 9, 14, 19, 24),
(5, 10, 15, 20, 25)]
In [483]: alist3=[l[::-1] for l in alist2]
In [484]: alist3
Out[484]:
[(21, 16, 11, 6, 1),
(22, 17, 12, 7, 2),
(23, 18, 13, 8, 3),
(24, 19, 14, 9, 4),
(25, 20, 15, 10, 5)]
或一行:
[list(l[::-1]) for l in zip(*alist1)]
需要内部list
来制作列表而不是元组列表。
此list
代码可用于&#39;矩阵&#39;不是正方形。但它正在制作一些列表的副本。但这是典型的Python列表方式。从旧的(通过列表推导)创建新列表几乎总是比改变原始列表更容易。您的rotate
函数证明了这一点。我无法一眼就看出正在做什么。您有n//2
和m-1-i
这样的模糊范围。并且您无法处理n
和m
不同的情况(因此生成的外部列表的长度与原始列表的长度不同)。
请记住,列表包含指针,而不是值。 A&#39;矩阵&#39; list只是一个列表,其中包含指向其他列表的指针,这些列表本身指向存储在内存中的值。
=======================
In [493]: %%timeit alist=arr.tolist()
...: rotate(alist)
10000 loops, best of 3: 21 µs per loop
In [494]: %%timeit alist=arr.tolist()
...: [list(row[::-1]) for row in zip(*alist)]
100000 loops, best of 3: 6.83 µs per loop
纯数组操作要快得多:
In [495]: timeit arr.transpose()[:,::-1]
The slowest run took 11.51 times longer....
1000000 loops, best of 3: 1.46 µs per loop
但是将嵌套列表转换为数组确实需要时间
In [496]: %%timeit alist=arr.tolist()
...: np.array(alist).transpose()[:,::-1]
...:
100000 loops, best of 3: 11.7 µs per loop
为了比较,direct
numpy函数 - 这个数组足够小,以至于几层函数调用会占用大量时间。
In [523]: timeit np.rot90(arr,-1)
The slowest run took 5.06 times longer ...
100000 loops, best of 3: 6.18 µs per loop
我认为,对于较大的数组,in-place
rotate
会相对更差 - 直到其他人生成MemoryErrors
。
答案 2 :(得分:1)
martianwars' answer很棒。然而,与NumPy有一条更直接的路线:
np.rot90(arr)
唯一美中不足的是rot90逆时针旋转数组 而OP的例子似乎需要顺时针。不过没问题。有一个参数k
表示旋转到(逻辑上)的次数。对于i
顺时针旋转:
np.rot90(arr, 4-i)
答案 3 :(得分:0)
要将二维矩阵顺时针旋转90°(不分配新矩阵),它将包括以下两个步骤。
"""Example:
[ [ [
[1,2,3], [7,8,9] [7,4,1]
[4,5,6], -> [4,5,6] -> [8,5,2]
[7,8,9] [1,2,3] [9,6,3]
] ] ]
"""
def rotate(matrix):
rows = len(matrix)
columns = len(matrix[0])
# inverse row
for i in range(rows//2):
matrix[i], matrix[columns-i-1] =\
matrix[columns-i-1], matrix[i]
# transpose
for i in range(rows):
for j in range(i):
matrix[i][j], matrix[j][i] =\
matrix[j][i], matrix[i][j]
if __name__ == "__main__":
matrix = [[1,2,3],[4,5,6],[7,8,9]]
rotate(matrix)
print(matrix)