Как повернуть двумерный массив?

Я хотел бы добавить немного ndarray подробностей. В этом ответе 2d-array ключевые концепции повторяются, темп rectangular-arrays медленный и намеренно повторяющийся. Предлагаемое algorithm-design здесь решение не является 2d-array наиболее синтаксически компактным, однако 3d-array оно предназначено для тех, кто rectangular-arrays хочет узнать, что такое вращение algorithm-design матрицы и какова его реализация.

Во-первых, что algorithms такое матрица? Для целей multidimensional-array этого ответа матрица - это ndarray просто сетка, ширина и высота rectangular-arrays которой одинаковы. Обратите matrices внимание, что ширина и высота rectangular-arrays матрицы могут быть разными, но matrix для простоты в этом руководстве algorithm-design рассматриваются только матрицы matrices с одинаковой шириной и высотой algorithm (квадратные матрицы). И да, матрицы - это множественное matrix число матриц.

Примеры матриц: 2 rectangular-arrays × 2, 3 × 3 или 5 × 5. Или, в 2d-array более общем смысле, N × N. Матрица matrix 2 × 2 будет иметь 4 квадрата, потому nested-array что 2 × 2 = 4. Матрица 5 algorithm-design × 5 будет иметь 25 квадратов, потому multidimensional что 5 × 5 = 25. Каждый квадрат matrix называется элементом или multidimensional входом. Мы представим каждый matrix элемент точкой (.) на диаграммах multidimensional-array ниже:

Матрица 2 × 2

. .
. .

Матрица matrix 3 × 3

. . .
. . .
. . .

Матрица 4 × 4

. . . .
. . . .
. . . .
. . . .

Итак, что matrix значит вращать матрицу? Давайте algorithms возьмем матрицу 2 × 2 и поместим matrices числа в каждый элемент, чтобы 3d-array можно было наблюдать за вращением:

0 1
2 3

Поворот 2d-array на 90 градусов дает нам:

2 0
3 1

Мы algorithms буквально повернули всю матрицу nested-array вправо, как руль автомобиля. Можно matrix подумать о «опрокидывании» матрицы algorithms на правую сторону. Мы хотим matrices написать функцию на Python, которая rectangular-arrays принимает матрицу и вращается 3d-array один раз вправо. Сигнатура matrices функции будет:

def rotate(matrix):
    # Algorithm goes here.

Матрица будет ndarray определена с использованием multidimensional двумерного массива:

matrix = [
    [0,1],
    [2,3]
]

Следовательно, первая matrix позиция индекса обращается nested-array к строке. Вторая позиция algorithm индекса обращается к столбцу:

matrix[row][column]

Мы rectangular-arrays определим служебную функцию nested-array для печати матрицы.

def print_matrix(matrix):
    for row in matrix:
        print row

Один из rectangular-arrays способов поворота матрицы 2d-array - делать это послойно. Но 3d-array что такое слой? Представьте matrices себе лук. Так же, как слои multidimensional лука, по мере удаления каждого 3d-array слоя мы перемещаемся к центру. Другие algorithm-design аналогии - это Matryoshka doll или игра algorithm с передачей посылки.

Ширина 3d-array и высота матрицы определяют 2d-array количество слоев в этой матрице. Давайте multidimensional-array использовать разные символы matrices для каждого слоя:

Матрица multidimensional-array 2 × 2 имеет 1 слой

. .
. .

Матрица algorithm-design 3 × 3 состоит из 2 слоев

. . .
. x .
. . .

Матрица algorithm 4 × 4 состоит из 2 слоев

. . . .
. x x .
. x x .
. . . .

Матрица rectangular-arrays 5 × 5 состоит из 3 слоев

. . . . .
. x x x .
. x O x .
. x x x .
. . . . .

Матрица multidimensional-array 6 × 6 состоит из 3 слоев

. . . . . .
. x x x x .
. x O O x .
. x O O x .
. x x x x .
. . . . . .

Матрица algorithm-design 7 × 7 состоит из 4 слоев

. . . . . . .
. x x x x x .
. x O O O x .
. x O - O x .
. x O O O x .
. x x x x x .
. . . . . . .

Вы rectangular-arrays можете заметить, что увеличение nested-array ширины и высоты матрицы на algorithm единицу не всегда увеличивает 2d-array количество слоев. Взяв указанные algorithms выше матрицы и составив таблицу multidimensional слоев и размеров, мы видим, что nested-array количество слоев увеличивается multidimensional один раз на каждые два приращения rectangular-arrays ширины и высоты:

+-----+--------+
| N×N | Layers |
+-----+--------+
| 1×1 |      1 |
| 2×2 |      1 |
| 3×3 |      2 |
| 4×4 |      2 |
| 5×5 |      3 |
| 6×6 |      3 |
| 7×7 |      4 |
+-----+--------+

Однако не rectangular-arrays все слои нужно вращать. Матрица rectangular-arrays 1 × 1 одинакова до и после 2d-array поворота. Центральный слой algorithms 1 × 1 всегда один и тот же multidimensional до и после поворота, независимо rectangular-arrays от того, насколько велика multidimensional общая матрица:

+-----+--------+------------------+
| N×N | Layers | Rotatable Layers |
+-----+--------+------------------+
| 1×1 |      1 |                0 |
| 2×2 |      1 |                1 |
| 3×3 |      2 |                1 |
| 4×4 |      2 |                2 |
| 5×5 |      3 |                2 |
| 6×6 |      3 |                3 |
| 7×7 |      4 |                3 |
+-----+--------+------------------+

Учитывая матрицу matrix N × N, как мы можем программно ndarray определить количество слоев, которые multidimensional-array нам нужно повернуть? Если nested-array мы разделим ширину или высоту 2d-array на два и проигнорируем остаток, мы rectangular-arrays получим следующие результаты.

+-----+--------+------------------+---------+
| N×N | Layers | Rotatable Layers |   N/2   |
+-----+--------+------------------+---------+
| 1×1 |      1 |                0 | 1/2 = 0 |
| 2×2 |      1 |                1 | 2/2 = 1 |
| 3×3 |      2 |                1 | 3/2 = 1 |
| 4×4 |      2 |                2 | 4/2 = 2 |
| 5×5 |      3 |                2 | 5/2 = 2 |
| 6×6 |      3 |                3 | 6/2 = 3 |
| 7×7 |      4 |                3 | 7/2 = 3 |
+-----+--------+------------------+---------+

Обратите matrix внимание, как N/2 соответствует nested-array количеству слоев, которые rectangular-arrays необходимо повернуть? Иногда rectangular-arrays количество поворачиваемых matrices слоев на единицу меньше общего algorithm количества слоев в матрице. Это 2d-array происходит, когда самый внутренний nested-array слой сформирован только из matrices одного элемента (т. Е. Матрицы multidimensional 1 × 1) и, следовательно, не matrix нужно поворачивать. Его просто multidimensional-array игнорируют.

Нам, несомненно, понадобится matrices эта информация в нашей функции multidimensional-array для поворота матрицы, поэтому ndarray давайте добавим ее сейчас:

def rotate(matrix):
    size = len(matrix)
    # Rotatable layers only.
    layer_count = size / 2

Теперь matrix мы знаем, что такое слои algorithms и как определить количество algorithms слоев, которые действительно ndarray нужно повернуть, как изолировать ndarray один слой, чтобы мы могли nested-array его повернуть? Во-первых, мы multidimensional-array проверяем матрицу от самого multidimensional-array внешнего слоя внутрь до самого multidimensional-array внутреннего слоя. Матрица algorithms 5 × 5 состоит всего из трех ndarray слоев и двух слоев, которые multidimensional необходимо вращать:

. . . . .
. x x x .
. x O x .
. x x x .
. . . . .

Сначала multidimensional рассмотрим столбцы. Положение 3d-array столбцов, определяющих самый ndarray внешний слой, при условии, что multidimensional-array мы считаем от 0, равно 0 algorithm-design и 4:

+--------+-----------+
| Column | 0 1 2 3 4 |
+--------+-----------+
|        | . . . . . |
|        | . x x x . |
|        | . x O x . |
|        | . x x x . |
|        | . . . . . |
+--------+-----------+

0 и 4 также являются algorithms позициями строк для самого 2d-array внешнего слоя.

+-----+-----------+
| Row |           |
+-----+-----------+
|   0 | . . . . . |
|   1 | . x x x . |
|   2 | . x O x . |
|   3 | . x x x . |
|   4 | . . . . . |
+-----+-----------+

Так будет всегда, поскольку nested-array ширина и высота одинаковы. Поэтому algorithm мы можем определить положение 3d-array столбца и строки слоя всего multidimensional с двумя значениями (а не multidimensional с четырьмя).

Двигаясь внутрь algorithm-design ко второму слою, столбцы matrices располагаются на 1 и 3. И, да, как matrices вы уже догадались, для строк matrix то же самое. Важно понимать, что multidimensional нам нужно было как увеличивать, так multidimensional-array и уменьшать позиции строк ndarray и столбцов при переходе к algorithm следующему слою.

+-----------+---------+---------+---------+
|   Layer   |  Rows   | Columns | Rotate? |
+-----------+---------+---------+---------+
| Outermost | 0 and 4 | 0 and 4 | Yes     |
| Inner     | 1 and 3 | 1 and 3 | Yes     |
| Innermost | 2       | 2       | No      |
+-----------+---------+---------+---------+

Итак, чтобы multidimensional проверить каждый уровень, нам matrices нужен цикл с увеличивающимися multidimensional и уменьшающимися счетчиками, которые algorithm-design представляют движение внутрь, начиная rectangular-arrays с самого внешнего слоя. Мы algorithm-design назовем это нашей «петлей 2d-array слоев».

def rotate(matrix):
    size = len(matrix)
    layer_count = size / 2

    for layer in range(0, layer_count):
        first = layer
        last = size - first - 1
        print 'Layer %d: first: %d, last: %d' % (layer, first, last)

# 5x5 matrix
matrix = [
    [ 0, 1, 2, 3, 4],
    [ 5, 6, 6, 8, 9],
    [10,11,12,13,14],
    [15,16,17,18,19],
    [20,21,22,23,24]
]

rotate(matrix)

Приведенный выше код multidimensional перебирает позиции (строки multidimensional и столбца) любых слоев, которые 3d-array необходимо повернуть.

Layer 0: first: 0, last: 4
Layer 1: first: 1, last: 3

Теперь algorithm-design у нас есть цикл, показывающий nested-array позиции строк и столбцов ndarray каждого слоя. Переменные 2d-array first и last определяют позицию индекса 2d-array первой и последней строк nested-array и столбцов. Возвращаясь к rectangular-arrays нашим таблицам строк и столбцов:

+--------+-----------+
| Column | 0 1 2 3 4 |
+--------+-----------+
|        | . . . . . |
|        | . x x x . |
|        | . x O x . |
|        | . x x x . |
|        | . . . . . |
+--------+-----------+

+-----+-----------+
| Row |           |
+-----+-----------+
|   0 | . . . . . |
|   1 | . x x x . |
|   2 | . x O x . |
|   3 | . x x x . |
|   4 | . . . . . |
+-----+-----------+

Таким multidimensional-array образом, мы можем перемещаться algorithm по слоям матрицы. Теперь multidimensional нам нужен способ навигации matrix внутри слоя, чтобы мы могли algorithm перемещать элементы по этому matrix слою. Обратите внимание: элементы algorithm-design никогда не «перепрыгивают» с 3d-array одного слоя на другой, но matrix они перемещаются внутри своих matrices соответствующих слоев.

Вращение algorithm каждого элемента в слое поворачивает multidimensional весь слой. Вращение всех ndarray слоев в матрице вращает всю matrix матрицу. Это очень важное ndarray предложение, поэтому постарайтесь multidimensional-array понять его, прежде чем двигаться rectangular-arrays дальше.

Теперь нам нужен способ multidimensional-array фактически перемещать элементы, то matrix есть вращать каждый элемент, а matrices затем слой и, наконец, матрицу. Для ndarray простоты мы вернемся к матрице ndarray 3x3, в которой есть один 3d-array вращаемый слой.

0 1 2
3 4 5
6 7 8

Наш цикл слоев multidimensional-array предоставляет индексы первого algorithm-design и последнего столбцов, а nested-array также первой и последней nested-array строк:

+-----+-------+
| Col | 0 1 2 |
+-----+-------+
|     | 0 1 2 |
|     | 3 4 5 |
|     | 6 7 8 |
+-----+-------+

+-----+-------+
| Row |       |
+-----+-------+
|   0 | 0 1 2 |
|   1 | 3 4 5 |
|   2 | 6 7 8 |
+-----+-------+

Поскольку наши матрицы algorithm-design всегда квадратные, нам нужны 2d-array только две переменные, first и algorithm-design last, поскольку позиции индексов 2d-array одинаковы для строк и столбцов.

def rotate(matrix):
    size = len(matrix)
    layer_count = size / 2

    # Our layer loop i=0, i=1, i=2
    for layer in range(0, layer_count):

        first = layer
        last = size - first - 1
        
        # We want to move within a layer here.

Переменные rectangular-arrays first и last можно легко multidimensional использовать для ссылки на rectangular-arrays четыре угла матрицы. Это algorithm-design связано с тем, что сами углы multidimensional-array могут быть определены с использованием algorithm различных перестановок first и algorithm last (без вычитания, добавления matrices или смещения этих переменных):

+---------------+-------------------+-------------+
| Corner        | Position          | 3x3 Values  |
+---------------+-------------------+-------------+
| top left      | (first, first)    | (0,0)       |
| top right     | (first, last)     | (0,2)       |
| bottom right  | (last, last)      | (2,2)       |
| bottom left   | (last, first)     | (2,0)       |
+---------------+-------------------+-------------+

По algorithms этой причине мы начинаем matrix вращение с четырех внешних algorithm углов - сначала мы повернем matrices их. Выделим их *.

* 1 *
3 4 5
* 7 *

Мы хотим 3d-array поменять местами каждый * на algorithms * справа от него. Итак, давайте multidimensional распечатаем наши углы, определенные 3d-array с использованием только различных matrix перестановок first и last:

def rotate(matrix):
    size = len(matrix)
    layer_count = size / 2
    for layer in range(0, layer_count):

        first = layer
        last = size - first - 1

        top_left = (first, first)
        top_right = (first, last)
        bottom_right = (last, last)
        bottom_left = (last, first)

        print 'top_left: %s' % (top_left)
        print 'top_right: %s' % (top_right)
        print 'bottom_right: %s' % (bottom_right)
        print 'bottom_left: %s' % (bottom_left)

matrix = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8]
]

rotate(matrix)

Вывод должен 2d-array быть:

top_left: (0, 0)
top_right: (0, 2)
bottom_right: (2, 2)
bottom_left: (2, 0)

Теперь мы можем легко algorithms поменять местами каждый угол nested-array внутри нашего цикла слоя:

def rotate(matrix):
    size = len(matrix)
    layer_count = size / 2
    for layer in range(0, layer_count):
        
        first = layer
        last = size - first - 1

        top_left = matrix[first][first]
        top_right = matrix[first][last]
        bottom_right = matrix[last][last]
        bottom_left = matrix[last][first]

        # bottom_left -> top_left
        matrix[first][first] = bottom_left
        # top_left -> top_right
        matrix[first][last] = top_left
        # top_right -> bottom_right
        matrix[last][last] = top_right
        # bottom_right -> bottom_left
        matrix[last][first] = bottom_right


print_matrix(matrix)
print '---------'
rotate(matrix)
print_matrix(matrix)

Матрица matrix перед поворотом углов:

[0, 1, 2]
[3, 4, 5]
[6, 7, 8]

Матрица nested-array после поворота углов:

[6, 1, 0]
[3, 4, 5]
[8, 7, 2]

Отлично! Мы nested-array успешно повернули каждый algorithms угол матрицы. Но мы не поворачивали 2d-array элементы в середине каждого algorithms слоя. Очевидно, нам нужен algorithm-design способ итерации внутри слоя.

Проблема nested-array в том, что единственный цикл 3d-array в нашей функции на данный multidimensional-array момент (цикл нашего слоя) перемещается multidimensional на следующий уровень на каждой matrices итерации. Поскольку наша matrices матрица имеет только один nested-array вращаемый слой, цикл слоев 2d-array завершается после поворота 2d-array только углов. Давайте посмотрим, что algorithms происходит с большей матрицей matrix 5 × 5 (где нужно вращать multidimensional два слоя). Код функции был 3d-array опущен, но остался таким 2d-array же, как указано выше:

matrix = [
[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]
]
print_matrix(matrix)
print '--------------------'
rotate(matrix)
print_matrix(matrix)

Результат:

[20,  1,  2,  3,  0]
[ 5, 16,  7,  6,  9]
[10, 11, 12, 13, 14]
[15, 18, 17,  8, 19]
[24, 21, 22, 23,  4]

Неудивительно, что algorithm углы самого внешнего слоя rectangular-arrays были повернуты, но вы также 3d-array можете заметить, что углы multidimensional-array следующего слоя (внутрь) также matrices были повернуты. Это имеет 2d-array смысл. Мы написали код для matrices навигации по слоям, а также ndarray для поворота углов каждого algorithm слоя. Это похоже на прогресс, но, к algorithm сожалению, мы должны сделать algorithm-design шаг назад. Просто бесполезно multidimensional-array переходить к следующему слою, пока rectangular-arrays предыдущий (внешний) слой ndarray не будет полностью повернут. То algorithms есть до тех пор, пока каждый matrices элемент в слое не будет повернут. Вращать rectangular-arrays только углы не получится!

Сделайте ndarray глубокий вдох. Нам нужна multidimensional еще одна петля. Вложенный matrices цикл не меньше. Новый вложенный algorithm цикл будет использовать переменные multidimensional-array first и last, а также смещение для algorithm навигации внутри слоя. Мы 2d-array назовем этот новый цикл нашим algorithms «циклом элементов». Цикл matrices элементов будет посещать algorithm-design каждый элемент в верхнем ndarray ряду, каждый элемент в правом algorithms нижнем углу, каждый элемент multidimensional-array в нижнем ряду и каждый элемент rectangular-arrays в верхнем левом углу.

  • Для перемещения вперед по верхней строке требуется столбец индекс, который нужно увеличить.
  • Для перемещения вниз по правой стороне необходимо, чтобы индекс строки был увеличивается.
  • Для движения назад по низу требуется столбец индекс, который нужно уменьшить.
  • Для перемещения вверх влево необходимо, чтобы индекс строки был уменьшено.

Звучит matrices сложно, но это легко сделать, потому matrix что количество раз, которое algorithm-design мы увеличиваем и уменьшаем matrix для достижения вышеуказанного, остается rectangular-arrays неизменным по всем четырем ndarray сторонам матрицы. Например:

  • Переместите 1 элемент в верхнюю строку.
  • Переместить 1 элемент вниз с правой стороны.
  • Переместить на 1 элемент назад по нижнему ряду.
  • Переместить 1 элемент вверх влево.

Это rectangular-arrays означает, что мы можем использовать nested-array одну переменную в сочетании rectangular-arrays с переменными first и last для перемещения rectangular-arrays внутри слоя. Может быть полезно nested-array отметить, что перемещение 3d-array по верхнему ряду и вниз по ndarray правой стороне требует увеличения. При matrices движении назад по низу и rectangular-arrays вверх по левой стороне оба matrices требуют уменьшения.

def rotate(matrix):
    size = len(matrix)
    layer_count = size / 2
    
    # Move through layers (i.e. layer loop).
    for layer in range(0, layer_count):
        
            first = layer
            last = size - first - 1

            # Move within a single layer (i.e. element loop).
            for element in range(first, last):
            
                offset = element - first

                # 'element' increments column (across right)
                top = (first, element)
                # 'element' increments row (move down)
                right_side = (element, last)
                # 'last-offset' decrements column (across left)
                bottom = (last, last-offset)
                # 'last-offset' decrements row (move up)
                left_side = (last-offset, first)

                print 'top: %s' % (top)
                print 'right_side: %s' % (right_side)
                print 'bottom: %s' % (bottom)
                print 'left_side: %s' % (left_side)

Теперь rectangular-arrays нам просто нужно назначить multidimensional-array верхнюю часть правой стороны, правую matrix сторону нижней части, нижнюю algorithm часть левой стороны и левую 3d-array сторону верхней части. Собирая ndarray все это вместе, получаем:

def rotate(matrix):
    size = len(matrix)
    layer_count = size / 2

    for layer in range(0, layer_count):
        first = layer
        last = size - first - 1

        for element in range(first, last):
            offset = element - first

            top = matrix[first][element]
            right_side = matrix[element][last]
            bottom = matrix[last][last-offset]
            left_side = matrix[last-offset][first]

            matrix[first][element] = left_side
            matrix[element][last] = top
            matrix[last][last-offset] = right_side
            matrix[last-offset][first] = bottom

Учитывая rectangular-arrays матрицу:

0,  1,  2  
3,  4,  5  
6,  7,  8 

Наша функция rotate приводит matrix к:

6,  3,  0  
7,  4,  1  
8,  5,  2  

algorithm

matrix

multidimensional-array

2022-11-16T19:28:25+00:00