Вращение двумерного массива в Python
В программе, которую я пишу, нужно повернуть двумерный массив. В поисках оптимального решения я нашел этот впечатляющий однострочный лайнер, который выполняет эту работу:
rotated = zip(*original[::-1])
Я использую его в своей программе сейчас, и он работает так, как предполагалось. Моя проблема заключается в том, что я не понимаю, как это работает.
Я был бы признателен, если бы кто-нибудь мог объяснить, как различные функции участвуют в достижении желаемого результата.
Ответы
Ответ 1
Рассмотрим следующий двумерный список:
original = [[1, 2],
[3, 4]]
Пошаговое разбиение:
>>> original[::-1] # elements of original are reversed
[[3, 4], [1, 2]]
Этот список передается в zip()
с помощью распаковки аргументов, поэтому вызов zip
заканчивается как эквивалент этого:
zip([3, 4],
[1, 2])
# ^ ^----column 2
# |-------column 1
# returns [(3, 1), (4, 2)], which is a original rotated clockwise
Надеемся, что комментарии дают понять, что делает zip
, он будет группировать элементы из каждого входного итерабельного на основе индекса, или, другими словами, группирует столбцы.
Ответ 2
Это умный бит. Здесь разбивка:
-
[::-1]
- делает мелкую копию исходного списка в обратном порядке. Также можно использовать reversed()
, который приведет к обратному итератору по списку, а не к фактическому копированию списка (более эффективный объем памяти).
-
*
- делает каждый подписок в исходном списке отдельным аргументом zip()
(т.е. распаковывает список)
-
zip()
- переносит один элемент из каждого аргумента и выводит список (ну, кортеж) из них и повторяется до тех пор, пока все подсписки не будут исчерпаны. Здесь фактически происходит перенос.
Итак, предположим, что у вас есть это:
[ [1, 2, 3],
[4, 5, 6],
[7, 8, 9] ]
Сначала вы получите это (мелкая, обратная копия):
[ [7, 8, 9],
[4, 5, 6],
[1, 2, 3] ]
Затем каждый из подписок передается как аргумент zip
:
zip([7, 8, 9], [4, 5, 6], [1, 2, 3])
zip()
многократно потребляет один элемент с начала каждого из его аргументов и делает из него кортеж, пока не будет больше элементов, в результате чего:
[(7, 4, 1),
(8, 5, 2),
(9, 6, 3)]
И Боб твой дядя.
Чтобы ответить на вопрос @IkeMiguel в комментарии о его вращении в другом направлении, это довольно просто: вам просто нужно отменить обе последовательности, которые входят в zip
и результат. Первое может быть достигнуто путем удаления [::-1]
, а второе может быть достигнуто путем выброса reversed()
вокруг всего. Поскольку reversed()
возвращает итератор по списку, нам нужно будет поместить list()
вокруг него для его преобразования. Итак:
rotated = list(zip(*reversed(original)))
Конечно, вы также можете просто повернуть список по часовой стрелке три раза.: -)
Ответ 3
Есть три части:
- original [:: - 1] изменяет исходный массив. Это обозначение - нарезка списка Python. Это дает вам "подсписку" исходного списка, описанного в [start: end: step], start - это первый элемент, end - последний элемент, который будет использоваться в подсписке. шаг говорит, что каждый элемент шага от первого до последнего. Пропущенный старт и конец означает, что срез будет полным списком, а отрицательный шаг означает, что вы получите элементы в обратном порядке. Так, например, если оригинал был [x, y, z], результатом будет [z, y, x]
- * При переходе списка/кортежа в список аргументов вызова функции означает "развернуть" список/кортеж, чтобы каждый из его элементов стал отдельным аргументом функции, а не самим списком/кортежем. Итак, если, скажем, args = [1,2,3], то zip (args) совпадает с zip ([1,2,3]), но zip (* args) совпадает с zip (1, 2,3).
- zip - это функция, которая принимает n аргументов, каждая из которых имеет длину m и создает список длины m, элементы из которых имеют длину n и содержат соответствующие элементы каждого из исходных списков. Например, zip ([1,2], [a, b], [x, y]) является [[1, a, x], [2, b, y]]]. См. Также Документация Python.
Ответ 4
У меня была эта проблема, и я нашел большую страницу по википедии по этому вопросу (в разделе "Общие повороты":
https://en.wikipedia.org/wiki/Rotation_matrix#Ambiguities
Затем я написал следующий код, супер подробный, чтобы иметь четкое представление о том, что происходит.
Я надеюсь, что вам будет полезно копать больше в очень красивом и умном однострочном лайнере, который вы разместили.
Чтобы быстро протестировать его, вы можете скопировать/вставить его здесь:
http://www.codeskulptor.org/
triangle = [[0,0],[5,0],[5,2]]
coordinates_a = triangle[0]
coordinates_b = triangle[1]
coordinates_c = triangle[2]
def rotate90ccw(coordinates):
print "Start coordinates:"
print coordinates
old_x = coordinates[0]
old_y = coordinates[1]
# Here we apply the matrix coming from Wikipedia
# for 90 ccw it looks like:
# 0,-1
# 1,0
# What does this mean?
#
# Basically this is how the calculation of the new_x and new_y is happening:
# new_x = (0)(old_x)+(-1)(old_y)
# new_y = (1)(old_x)+(0)(old_y)
#
# If you check the lonely numbers between parenthesis the Wikipedia matrix numbers
# finally start making sense.
# All the rest is standard formula, the same behaviour will apply to other rotations, just
# remember to use the other rotation matrix values available on Wiki for 180ccw and 170ccw
new_x = -old_y
new_y = old_x
print "End coordinates:"
print [new_x, new_y]
def rotate180ccw(coordinates):
print "Start coordinates:"
print coordinates
old_x = coordinates[0]
old_y = coordinates[1]
new_x = -old_x
new_y = -old_y
print "End coordinates:"
print [new_x, new_y]
def rotate270ccw(coordinates):
print "Start coordinates:"
print coordinates
old_x = coordinates[0]
old_y = coordinates[1]
new_x = -old_x
new_y = -old_y
print "End coordinates:"
print [new_x, new_y]
print "Let rotate point A 90 degrees ccw:"
rotate90ccw(coordinates_a)
print "Let rotate point B 90 degrees ccw:"
rotate90ccw(coordinates_b)
print "Let rotate point C 90 degrees ccw:"
rotate90ccw(coordinates_c)
print "=== === === === === === === === === "
print "Let rotate point A 180 degrees ccw:"
rotate180ccw(coordinates_a)
print "Let rotate point B 180 degrees ccw:"
rotate180ccw(coordinates_b)
print "Let rotate point C 180 degrees ccw:"
rotate180ccw(coordinates_c)
print "=== === === === === === === === === "
print "Let rotate point A 270 degrees ccw:"
rotate270ccw(coordinates_a)
print "Let rotate point B 270 degrees ccw:"
rotate270ccw(coordinates_b)
print "Let rotate point C 270 degrees ccw:"
rotate270ccw(coordinates_c)
print "=== === === === === === === === === "
Ответ 5
Просто наблюдение. Вход представляет собой список списков, но вывод из очень приятного решения: rotated = zip (* original [:: - 1]) возвращает список кортежей.
Это может быть или не быть проблемой.
Это, однако, легко исправлено:
original = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
def rotated(array_2d):
list_of_tuples = zip(*array_2d[::-1])
return [list(elem) for elem in list_of_tuples]
# return map(list, list_of_tuples)
print(list(rotated(original)))
# [[7, 4, 1], [8, 5, 2], [9, 6, 3]]
Список comp или карта преобразуют внутренние кортежи обратно в списки.
Ответ 6
def ruota_antiorario(matrix):
ruota=list(zip(*reversed(matrix)))
return[list(elemento) for elemento in ruota]
def ruota_orario(matrix):
ruota=list(zip(*reversed(matrix)))
return[list(elemento)[::-1] for elemento in ruota][::-1]
Ответ 7
import copy
def rotate(matrix):
data = copy.deepcopy(matrix)
for i in range(len(data)):
for j in range(i+1,len(data[i])):
temp=data[i][j]
data[i][j]=data[j][i]
data[j][i]=temp
return data
Ответ 8
Я занимался реализацией вложенных списков python и кодировал следующее, чтобы вернуть список столбцов в матрице, который аналогичен вращению матрицы:
def getColumns(matrix):
columns = [[row[col] for row in matrix] for col in range(len(matrix[1]))]
return columns