Как вы вращаете двумерный массив? - PullRequest
290 голосов
/ 04 сентября 2008

Вдохновленный Пост Раймонда Чена , скажем, у вас есть двумерный массив 4x4, напишите функцию, которая поворачивает его на 90 градусов. Раймонд ссылается на решение в псевдокоде, но я хотел бы увидеть некоторые реальные вещи.

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

становится:

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

Обновление : ответ Ника самый простой, но есть ли способ сделать это лучше, чем n ^ 2? Что, если матрица была 10000x10000?

Ответы [ 60 ]

368 голосов
/ 29 декабря 2011

O (n ^ 2) время и O (1) пространство алгоритм (без каких-либо обходных путей и ханж-панки!)

Поворот на +90:

  1. Транспонирование
  2. Перевернуть каждую строку

Поворот на -90:

Метод 1:

  1. Транспонирование
  2. Перевернуть каждый столбец

Метод 2:

  1. Перевернуть каждый ряд
  2. Транспонирование

Поворот на +180:

Метод 1 : Поворот на +90 дважды

Метод 2 : перевернуть каждую строку и затем перевернуть каждый столбец (Транспонировать)

Поворот на -180:

Метод 1 : Поворот на -90 дважды

Метод 2 : перевернуть каждый столбец и затем перевернуть каждую строку

Метод 3 : повернуть на +180, поскольку они одинаковы

159 голосов
/ 16 февраля 2016

Я хотел бы добавить немного больше деталей. В этом ответе ключевые понятия повторяются, темп медленный и намеренно повторяющийся. Представленное здесь решение не является наиболее синтаксически компактным, однако оно предназначено для тех, кто хочет узнать, что такое поворот матрицы и какова ее реализация.

Во-первых, что такое матрица? Для целей этого ответа матрица - это просто сетка, в которой ширина и высота одинаковы. Обратите внимание, что ширина и высота матрицы могут быть разными, но для простоты в этом руководстве рассматриваются только матрицы с одинаковой шириной и высотой ( квадратные матрицы ). И да, матрицы - это множественное число матрицы.

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

2 × 2 матрицы

. .
. .

3 × 3 матрицы

. . .
. . .
. . .

матрица 4 × 4

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

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

0 1
2 3

Поворот на 90 градусов дает нам:

2 0
3 1

Мы буквально повернули всю матрицу один раз вправо, точно так же, как поворачивали руль автомобиля. Это может помочь подумать о «опрокидывании» матрицы с правой стороны. Мы хотим написать функцию на Python, которая принимает матрицу и поворачивается один раз вправо. Подпись функции будет:

def rotate(matrix):
    # Algorithm goes here.

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

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

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

matrix[row][column]

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

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

Один из способов поворота матрицы состоит в том, чтобы сделать ее слоем за раз. Но что такое слой? Подумай о луке. Так же, как слои лука, так как каждый слой удаляется, мы движемся к центру. Другие аналогии - это Матрешка или игра в посылку.

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

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

. .
. .

Матрица 3 × 3 имеет 2 слоя

. . .
. x .
. . .

Матрица 4 × 4 имеет 2 слоя

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

Матрица 5 × 5 имеет 3 слоя

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

Матрица 6 × 6 имеет 3 слоя

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

Матрица 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 .
. . . . . . .

Вы можете заметить, что увеличение ширины и высоты матрицы на единицу не всегда увеличивает количество слоев. Взяв вышеприведенные матрицы и таблицы слоев и размеров, мы видим, что количество слоев увеличивается один раз на каждые два приращения ширины и высоты:

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

Однако не все слои нуждаются во вращении. Матрица 1 × 1 одинакова до и после вращения. Центральный слой 1 × 1 всегда одинаков до и после вращения, независимо от размера всей матрицы:

+-----+--------+------------------+
| 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 |
+-----+--------+------------------+

Учитывая матрицу N × N, как мы можем программно определить количество слоев, которые нам нужно повернуть? Если мы разделим ширину или высоту на два и проигнорируем остаток, мы получим следующие результаты.

+-----+--------+------------------+---------+
| 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 |
+-----+--------+------------------+---------+

Обратите внимание, как N/2 соответствует количеству слоев, которые необходимо повернуть? Иногда количество вращающихся слоев на единицу меньше общего количества слоев в матрице. Это происходит, когда самый внутренний слой сформирован только из одного элемента (то есть матрицы 1 × 1) и, следовательно, не нуждается во вращении. Это просто игнорируется.

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

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

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

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

Давайте сначала посмотрим на столбцы. Положение столбцов, определяющих самый внешний слой, при условии, что мы считаем от 0, равно 0 и 4:

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

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

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

Это всегда будет так, поскольку ширина и высота одинаковы. Поэтому мы можем определить положение столбца и строки слоя только с двумя значениями (а не с четырьмя).

При перемещении внутрь ко второму слою позиции столбцов 1 и 3. И, как вы уже догадались, для строк то же самое. Важно понимать, что мы должны увеличивать и уменьшать позиции строк и столбцов при переходе внутрь к следующему слою.

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

Итак, для осмотра каждого слоя нам нужен цикл с увеличивающимися и уменьшающимися счетчиками, которые представляют собой движение внутрь, начиная с самого внешнего слоя. Мы назовем это нашей «петлей слоя».

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)

Приведенный выше код перебирает позиции (строки и столбцы) любых слоев, которые нужно вращать.

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

Теперь у нас есть цикл, предоставляющий позиции строк и столбцов каждого слоя. Переменные first и last идентифицируют позицию индекса первой и последней строк и столбцов. Возвращаясь к нашим таблицам строк и столбцов:

+--------+-----------+
| 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 | . . . . . |
+-----+-----------+

Так что мы можем перемещаться по слоям матрицы. Теперь нам нужен способ навигации внутри слоя, чтобы мы могли перемещать элементы вокруг этого слоя. Обратите внимание, что элементы никогда не «перепрыгивают» с одного слоя на другой, но они перемещаются внутри соответствующих слоев.

Вращение каждого элемента в слое вращает весь слой. Вращение всех слоев в матрице вращает всю матрицу. Это предложение очень важно, поэтому, пожалуйста, постарайтесь понять его, прежде чем двигаться дальше.

Теперь нам нужен способ фактически перемещать элементы, то есть вращать каждый элемент, а затем слой и, в конечном итоге, матрицу. Для простоты мы вернемся к матрице 3х3 - с одним вращающимся слоем.

0 1 2
3 4 5
6 7 8

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

+-----+-------+
| 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 |
+-----+-------+

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

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.

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

+---------------+-------------------+-------------+
| 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)       |
+---------------+-------------------+-------------+

По этой причине мы начинаем наше вращение с внешних четырех углов - мы будем вращать их первыми. Давайте выделим их с помощью *.

* 1 *
3 4 5
* 7 *

Мы хотим поменять местами каждый * с * справа от него. Итак, давайте продолжим распечатывать наши углы, определенные только с использованием различных комбинаций 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)

Вывод должен быть:

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

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

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)

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

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

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

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

Отлично! Мы успешно повернули каждый угол матрицы. Но мы не вращали элементы в середине каждого слоя. Ясно, что нам нужен способ итерации внутри слоя.

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

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]

Не должно быть сюрпризом, что углы внешнего слоя были повернуты, но вы также можете заметить, что углы следующего слоя (внутрь) также были повернуты. Это имеет смысл. Мы написали код для навигации по слоям, а также для поворота углов каждого слоя. Это похоже на прогресс, но, к сожалению, мы должны сделать шаг назад. Просто переходить к следующему слою бесполезно, пока предыдущий (внешний) слой не будет полностью повернут. То есть, пока каждый элемент в слое не будет повернут. Вращать только углы не получится!

Сделайте глубокий вдох. Нам нужен еще один цикл. Вложенный цикл не меньше. Новый вложенный цикл будет использовать переменные first и last, а также смещение для навигации по слою. Мы назовем этот новый цикл нашим «элементным циклом». Цикл элементов будет посещать каждый элемент в верхнем ряду, каждый элемент в правой части, каждый элемент в нижнем ряду и каждый элемент в левой части.

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

Это звучит сложно, но это легко сделать, потому что количество раз, которое мы увеличиваем и уменьшаем для достижения вышеуказанного, остается одинаковым по всем четырем сторонам матрицы. Например:

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

Это означает, что мы можем использовать одну переменную в сочетании с переменными first и last для перемещения внутри слоя. Это может помочь заметить, что перемещение по верхнему ряду и вниз по правой стороне требует увеличения. При движении назад вдоль нижней и левой стороны оба требуют уменьшения.

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_element = (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)

Теперь нам просто нужно присвоить верхнюю часть правой стороне, правую сторону нижней части, нижнюю часть левой стороне и левую сторону верхней части. Собрав все это вместе, мы получим:

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

С учетом матрицы:

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

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

6,  3,  0  
7,  4,  1  
8,  5,  2  
137 голосов
/ 04 сентября 2008

Вот оно в C #

int[,] array = new int[4,4] {
    { 1,2,3,4 },
    { 5,6,7,8 },
    { 9,0,1,2 },
    { 3,4,5,6 }
};

int[,] rotated = RotateMatrix(array, 4);

static int[,] RotateMatrix(int[,] matrix, int n) {
    int[,] ret = new int[n, n];

    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            ret[i, j] = matrix[n - j - 1, i];
        }
    }

    return ret;
}
124 голосов
/ 30 января 2009

Python:

rotated = zip(*original[::-1])  # On Python 3, list(zip(*original[::-1]))

Дешево, я знаю.

И против часовой стрелки:

rotated_ccw = zip(*original)[::-1]  # On Python 3, list(zip(*original))[::-1]

Как это работает: (запрошено в комментариях)

zip(*original) поменяет оси двумерных массивов, сложив соответствующие элементы из списков в новые списки. (Оператор * указывает функции распределять содержащиеся списки по аргументам)

>>> zip(*[[1,2,3],[4,5,6],[7,8,9]])
[[1,4,7],[2,5,8],[3,6,9]]

Оператор [::-1] переворачивает элементы массива (см. Расширенные фрагменты ).

>>> [[1,2,3],[4,5,6],[7,8,9]][::-1]
[[7,8,9],[4,5,6],[1,2,3]]

Наконец, объединение двух приведет к преобразованию вращения.

Изменение размещения [::-1] приведет к инверсии списков на разных уровнях матрицы.

68 голосов
/ 04 сентября 2008

Вот тот, который выполняет вращение на месте, вместо использования совершенно нового массива для хранения результата. Я прекратил инициализацию массива и распечатал его. Это работает только для квадратных массивов, но они могут быть любого размера. Затраты памяти равны размеру одного элемента массива, поэтому вы можете вращать массив настолько, насколько вам нужно.

int a[4][4];
int n = 4;
int tmp;
for (int i = 0; i < n / 2; i++)
{
    for (int j = i; j < n - i - 1; j++)
    {
        tmp             = a[i][j];
        a[i][j]         = a[j][n-i-1];
        a[j][n-i-1]     = a[n-i-1][n-j-1];
        a[n-i-1][n-j-1] = a[n-j-1][i];
        a[n-j-1][i]     = tmp;
    }
}
38 голосов
/ 25 ноября 2013

Здесь много тонны хорошего кода, но я просто хочу показать, что происходит геометрически, чтобы вы могли немного лучше понять логику кода. Вот как бы я подошел к этому.

Прежде всего, не путайте это с транспозицией, которая очень проста.

Основная идея состоит в том, чтобы рассматривать его как слои, и мы вращаем один слой за раз.

скажем, у нас есть 4x4

1   2   3   4
5   6   7   8
9   10  11  12
13  14  15  16

после того, как мы повернем его по часовой стрелке на 90, мы получим

13  9   5   1
14  10  6   2   
15  11  7   3
16  12  8   4

так что давайте разберем это, сначала мы вращаем 4 угла по существу

1           4


13          16

затем мы вращаем следующий алмаз, который немного искажен

    2
            8
9       
        15

а затем 2-й перекошенный бриллиант

        3
5           
            12
    14

так, чтобы он позаботился о внешнем крае, по сути, мы делаем эту одну оболочку за раз до

наконец средний квадрат (или, если он нечетный, только последний элемент, который не двигается)

6   7
10  11

так что теперь давайте разберемся с индексами каждого слоя, предположим, что мы всегда работаем с самым внешним слоем, мы делаем

[0,0] -> [0,n-1], [0,n-1] -> [n-1,n-1], [n-1,n-1] -> [n-1,0], and [n-1,0] -> [0,0]
[0,1] -> [1,n-1], [1,n-2] -> [n-1,n-2], [n-1,n-2] -> [n-2,0], and [n-2,0] -> [0,1]
[0,2] -> [2,n-2], [2,n-2] -> [n-1,n-3], [n-1,n-3] -> [n-3,0], and [n-3,0] -> [0,2]

и так далее и тому подобное пока мы не на полпути через край

так в общем случае это

[0,i] -> [i,n-i], [i,n-i] -> [n-1,n-(i+1)], [n-1,n-(i+1)] -> [n-(i+1),0], and [n-(i+1),0] to [0,i]
35 голосов
/ 04 сентября 2008

Как я уже говорил в моем предыдущем посте, вот некоторый код на C #, который реализует поворот матрицы O (1) для матрицы любого размера. Для краткости и читабельности нет проверки ошибок или диапазона. Код:

static void Main (string [] args)
{
  int [,]
    //  create an arbitrary matrix
    m = {{0, 1}, {2, 3}, {4, 5}};

  Matrix
    //  create wrappers for the data
    m1 = new Matrix (m),
    m2 = new Matrix (m),
    m3 = new Matrix (m);

  //  rotate the matricies in various ways - all are O(1)
  m1.RotateClockwise90 ();
  m2.Rotate180 ();
  m3.RotateAnitclockwise90 ();

  //  output the result of transforms
  System.Diagnostics.Trace.WriteLine (m1.ToString ());
  System.Diagnostics.Trace.WriteLine (m2.ToString ());
  System.Diagnostics.Trace.WriteLine (m3.ToString ());
}

class Matrix
{
  enum Rotation
  {
    None,
    Clockwise90,
    Clockwise180,
    Clockwise270
  }

  public Matrix (int [,] matrix)
  {
    m_matrix = matrix;
    m_rotation = Rotation.None;
  }

  //  the transformation routines
  public void RotateClockwise90 ()
  {
    m_rotation = (Rotation) (((int) m_rotation + 1) & 3);
  }

  public void Rotate180 ()
  {
    m_rotation = (Rotation) (((int) m_rotation + 2) & 3);
  }

  public void RotateAnitclockwise90 ()
  {
    m_rotation = (Rotation) (((int) m_rotation + 3) & 3);
  }

  //  accessor property to make class look like a two dimensional array
  public int this [int row, int column]
  {
    get
    {
      int
        value = 0;

      switch (m_rotation)
      {
      case Rotation.None:
        value = m_matrix [row, column];
        break;

      case Rotation.Clockwise90:
        value = m_matrix [m_matrix.GetUpperBound (0) - column, row];
        break;

      case Rotation.Clockwise180:
        value = m_matrix [m_matrix.GetUpperBound (0) - row, m_matrix.GetUpperBound (1) - column];
        break;

      case Rotation.Clockwise270:
        value = m_matrix [column, m_matrix.GetUpperBound (1) - row];
        break;
      }

      return value;
    }

    set
    {
      switch (m_rotation)
      {
      case Rotation.None:
        m_matrix [row, column] = value;
        break;

      case Rotation.Clockwise90:
        m_matrix [m_matrix.GetUpperBound (0) - column, row] = value;
        break;

      case Rotation.Clockwise180:
        m_matrix [m_matrix.GetUpperBound (0) - row, m_matrix.GetUpperBound (1) - column] = value;
        break;

      case Rotation.Clockwise270:
        m_matrix [column, m_matrix.GetUpperBound (1) - row] = value;
        break;
      }
    }
  }

  //  creates a string with the matrix values
  public override string ToString ()
  {
    int
      num_rows = 0,
      num_columns = 0;

    switch (m_rotation)
    {
    case Rotation.None:
    case Rotation.Clockwise180:
      num_rows = m_matrix.GetUpperBound (0);
      num_columns = m_matrix.GetUpperBound (1);
      break;

    case Rotation.Clockwise90:
    case Rotation.Clockwise270:
      num_rows = m_matrix.GetUpperBound (1);
      num_columns = m_matrix.GetUpperBound (0);
      break;
    }

    StringBuilder
      output = new StringBuilder ();

    output.Append ("{");

    for (int row = 0 ; row <= num_rows ; ++row)
    {
      if (row != 0)
      {
        output.Append (", ");
      }

      output.Append ("{");

      for (int column = 0 ; column <= num_columns ; ++column)
      {
        if (column != 0)
        {
          output.Append (", ");
        }

        output.Append (this [row, column].ToString ());
      }

      output.Append ("}");
    }

    output.Append ("}");

    return output.ToString ();
  }

  int [,]
    //  the original matrix
    m_matrix;

  Rotation
    //  the current view of the matrix
    m_rotation;
}

ОК, я подниму руку, на самом деле при вращении он не вносит никаких изменений в исходный массив. Но в ОО-системе это не имеет значения, если объект выглядит так, как будто он повернут к клиентам класса. На данный момент класс Matrix использует ссылки на исходные данные массива, поэтому изменение любого значения m1 также изменит m2 и m3. Небольшое изменение в конструкторе для создания нового массива и копирования значений в него позволит разобраться.

23 голосов
/ 11 октября 2008

Хотя вращение данных на месте может быть необходимым (возможно, для обновления физически сохраненного представления), становится проще и, возможно, более производительным добавить слой косвенности к доступу к массиву, возможно, к интерфейсу:

interface IReadableMatrix
{
    int GetValue(int x, int y);
}

Если ваш Matrix уже реализует этот интерфейс, его можно повернуть с помощью декоратора класса, например:

class RotatedMatrix : IReadableMatrix
{
    private readonly IReadableMatrix _baseMatrix;

    public RotatedMatrix(IReadableMatrix baseMatrix)
    {
        _baseMatrix = baseMatrix;
    }

    int GetValue(int x, int y)
    {
        // transpose x and y dimensions
        return _baseMatrix(y, x);
    }
}

Поворот + 90 / -90 / 180 градусов, переворачивание по горизонтали / вертикали и масштабирование также могут быть достигнуты таким же образом.

Производительность должна быть измерена в вашем конкретном сценарии. Однако операция O (n ^ 2) теперь заменена вызовом O (1). Это вызов виртуального метода, который на медленнее, чем прямой доступ к массиву, поэтому он зависит от того, как часто вращаемый массив используется после поворота. Если он используется один раз, то этот подход определенно победит. Если его вращать, а затем использовать в длительной системе в течение нескольких дней, то вращение на месте может работать лучше. Это также зависит от того, можете ли вы принять предварительную стоимость.

Как и во всех вопросах производительности, измерять, измерять, измерять!

17 голосов
/ 07 июля 2009

Это лучшая версия на Java: я сделал это для матрицы с другой шириной и высотой

  • h здесь высота матрицы после вращения
  • здесь ширина матрицы после вращения

public int[][] rotateMatrixRight(int[][] matrix)
{
    /* W and H are already swapped */
    int w = matrix.length;
    int h = matrix[0].length;
    int[][] ret = new int[h][w];
    for (int i = 0; i < h; ++i) {
        for (int j = 0; j < w; ++j) {
            ret[i][j] = matrix[w - j - 1][i];
        }
    }
    return ret;
}


public int[][] rotateMatrixLeft(int[][] matrix)
{
    /* W and H are already swapped */
    int w = matrix.length;
    int h = matrix[0].length;   
    int[][] ret = new int[h][w];
    for (int i = 0; i < h; ++i) {
        for (int j = 0; j < w; ++j) {
            ret[i][j] = matrix[j][h - i - 1];
        }
    }
    return ret;
}

Этот код основан на сообщении Ника Берарди.

17 голосов
/ 26 августа 2010

Рубиновый путь: .transpose.map &:reverse

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...