8

Как сделать поворот матрицы в одну строчку без NumPy и циклов?

Например, если исходная матрица:

[[1, 2],
[3, 4]]

то результирующая должна быть:

[[3, 1],
[4, 2]]
0xdb
  • 51,614
Andrew Moon
  • 91
  • 1
  • 1
  • 3

2 Answers2

14

​Нашел довольно элегантный способ сделать поворот матрицы в одну строчку без numpy и циклов. В рунете ничего толкового не смог найти, может кому то поможет. Оригинал здесь: Линк на оригинал

rotated = zip(*original[::-1]) # Python 2
rotated = tuple(zip(*original[::-1])) # Python 3

Как это работает.

original = [[1, 2],
            [3, 4]]

Сначала работает реверс

 >>> original[::-1] 
    [[3, 4], [1, 2]]

И далее этот уже обернутый список передаётся функции zip()

zip([3, 4],
    [1, 2])
#    ^  ^----column 2
#    |-------column 1

Надеюсь, кому-то пригодится, а то начинают перебор через встроенные циклы и т.д.

jfs
  • 52,361
  • Если нам нужен полностью изменяемый объект, то, я полагаю, tuple'ы в list'ы можно превратить таким образом: list(map(list, zip(*original[::-1]))) – Lagrange Jul 05 '21 at 23:16
  • 2
    И для поворота против часовой стрелки: list(map(list, reversed(list(zip(*f))))) – Lagrange Jul 05 '21 at 23:18
7

Вполне себе рабочий вариант, если скорость не критична.

Но если есть требования по скорости обработки, то лучше все-таки воспользоваться NumPy.

Сравнение производительности для массива 100x100:

In [72]: a = np.random.randint(0, 99, (100, 100))

In [73]: m = a.tolist()

In [74]: a.shape
Out[74]: (100, 100)

In [75]: %timeit tuple(zip(*m[::-1]))
10000 loops, best of 3: 71 µs per loop

In [76]: %timeit np.rot90(a, 3)
The slowest run took 9.64 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 2.63 µs per loop

Сравнение производительности для массива 1000x1000:

In [77]: a = np.random.randint(0, 99, (1000, 1000))

In [78]: m = a.tolist()

In [79]: a.shape
Out[79]: (1000, 1000)

In [80]: %timeit tuple(zip(*m[::-1]))
10 loops, best of 3: 32.6 ms per loop

In [81]: %timeit np.rot90(a, 3)
The slowest run took 7.54 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 2.59 µs per loop

Сравнение производительности для массива 10000x10000:

In [82]: a = np.random.randint(0, 99, (10000, 10000))

In [83]: m = a.tolist()

In [84]: a.shape
Out[84]: (10000, 10000)

In [85]: %timeit tuple(zip(*m[::-1]))
1 loop, best of 3: 4.43 s per loop

In [86]: %timeit np.rot90(a, 3)
The slowest run took 11.29 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 2.59 µs per loop
MaxU - stand with Ukraine
  • 149,321
  • 12
  • 59
  • 132