2次元配列をどのように回転させますか?


302

Raymond Chenの投稿に触発されて、4x4の2次元配列があるとしたら、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の場合はどうなりますか?


99
どうすればn ^ 2未満で済むでしょうか?すべての要素を読み取って設定する必要があり、n ^ 2個の要素がある
erikkallen 2009年


9
あなたのnは何ですか?2D配列が正方形であるかどうかはわかりません(一般的なケースではありません!たとえば、ベクトルは1次元の行列です)。nは幅と高さであり、したがってn²要素があることを意味しているようです。nを要素の数にすると、n = w×hのほうが理にかなっています。
niXar

1
これを行う高速な方法は次のとおりです。行と列のインデックス(iとjなど)を格納します。転置には一定の時間がかかります(インデックスを交換するだけです:)。ローテーションでも同じことができます(インデックスで遊ぶ)。
saadtaame 2013年

4
n ^ 2が実行できない場合。各要素にアクセスするインターフェースを作成できます。次に、(i、j)を指定して、回転を(i、j)に適用し、回転した要素にアクセスして戻ります。最高のソリューションではないかもしれませんが、機能します。
混乱させる

回答:


140

ここでは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;
}

6
もちろん、O(1)メモリを使用するソリューションはどうでしょうか?
AlexeyMK 2008

20
ソリューションのスペースはO(n ^ 2)複雑です。より良くする必要がある
Kshitij Jain 2013

6
NXMマトリックスはどうですか?
Rohit 2014

18
複雑さは、配列の要素数に比例します。Nが要素数の場合、複雑度はO(N)です。Nが辺の長さである場合、はい、複雑度はO(N ^ 2)ですが、それでも最適です。すべての要素を少なくとも1回は読み取る必要があります。マトリックスの印刷も同じ複雑さ
Alejandro

6
-90度回転の場合:ret[i][j] = matrix[j][n - i - 1]
Duncan Luk

387

O(n ^ 2)時間およびO(1)空間アルゴリズム(回避策や不安定なものはありません!)

+90回転:

  1. 転置
  2. 各行を逆にする

-90回転:

方法1:

  1. 転置
  2. 各列を逆にする

方法2:

  1. 各行を逆にする
  2. 転置

+180回転:

方法1:+90回2回回転

方法2:各行を逆にし、次に各列を逆にします(転置)

-180回転:

方法1:2回-90回転

方法2:各列を逆にしてから各行を逆にする

方法3:同じなので+180回転


4
これは私にとって非常に役に立ちました。この操作の「[疑似]コードバージョン」を知っていれば、アルゴリズムを作成できました。ありがとう!
duma

13
私のお気に入りのSOの答えの1つです。非常に有益です!
g33kz0r 2013

2
興味のある方は、JavaScript実装JSFiddleをご覧ください
ポリワール氏2014

6
-90回転:(1)各行を逆にします。(2)転置。Haskell:rotateCW = map reverse . transposeandrotateCCW = transpose . map reverse
トーマス・エディング2014

5
180回転と-180回転の違いは何ですか?
銭チェン

178

もう少し詳しく説明します。この回答では、主要な概念が繰り返され、ペースは遅く、意図的に繰り返されます。ここで提供されるソリューションは、構文的に最もコンパクトではありませんが、行列の回転とその結果の実装について知りたい人を対象としています。

まず、行列とは何ですか?この回答では、マトリックスは幅と高さが同じグリッドにすぎません。行列の幅と高さは異なる場合がありますが、簡単にするために、このチュートリアルでは幅と高さが等しい行列正方行列)のみを考慮します。そして、はい、行列行列の複数形です。

マトリックスの例は、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.

行列は、2次元配列を使用して定義されます。

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

したがって、最初のインデックス位置が行にアクセスします。2番目のインデックス位置は列にアクセスします。

matrix[row][column]

マトリックスを出力するユーティリティ関数を定義します。

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

マトリックスを回転させる1つの方法は、一度にレイヤーを実行することです。しかし、レイヤーとは何ですか?タマネギについて考えてください。タマネギの層のように、各層が取り除かれると、中心に向かって移動します。他の類推は、マトリョーシカ人形または小包のゲームです。

マトリックスの幅と高さは、そのマトリックスのレイヤー数を決定します。各レイヤーに異なるシンボルを使用しましょう:

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

マトリックスの幅と高さを1ずつ増やしても、必ずしもレイヤー数が増えるわけではないことに気付くでしょう。上記の行列を取り、レイヤーと寸法を表にすると、レイヤーの数は、幅と高さの2つの増分ごとに1回増加します。

+-----+--------+
| 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の行列が与えられた場合、回転する必要があるレイヤーの数をプログラムでどのように決定できますか?幅または高さを2で除算し、残りを無視すると、次の結果が得られます。

+-----+--------+------------------+---------+
| 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つの要素(つまり、1x1マトリックス)のみで構成されているため、回転する必要がない場合に発生します。単に無視されます。

間違いなく、マトリックスを回転させるために関数にこの情報が必要になるので、ここで追加しましょう:

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

これで、レイヤーとは何か、回転が実際に必要なレイヤーの数を決定する方法、単一のレイヤーを分離して回転できるようにするにはどうすればよいですか?最初に、最も外側の層から内側に向​​かって、最も内側の層までマトリックスを検査します。5×5マトリックスには、合計3つのレイヤーと回転が必要な2つのレイヤーがあります。

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

幅と高さが同じであるため、これは常に当てはまります。したがって、レイヤーの列と行の位置を4つではなく2つの値で定義できます。

内側の2番目のレイヤーに移動すると、列の位置は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

これで、各レイヤーの行と列の位置を提供するループができました。変数firstlastは、最初と最後の行と列のインデックス位置を識別します。行と列のテーブルを再び参照します。

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

したがって、マトリックスのレイヤーをナビゲートできます。ここで、レイヤー内で要素を移動できるように、レイヤー内を移動する方法が必要です。要素があるレイヤーから別のレイヤーに「ジャンプ」することはありませんが、要素はそれぞれのレイヤー内で移動します。

レイヤー内の各要素を回転すると、レイヤー全体が回転します。マトリックス内のすべてのレイヤーを回転すると、マトリックス全体が回転します。この文章は非常に重要なので、先に進む前によく理解して理解してください。

ここで、実際に要素を移動する方法が必要です。つまり、各要素を回転させ、続いてレイヤー、そして最終的にはマトリックスを回転させます。簡単にするために、回転可能なレイヤーが1つある3x3マトリックスに戻します。

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

私たちの行列が常に正方形であるので、私たちは2つの変数を必要とする、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は、行列の四隅を参照するために簡単に使用できます。これは、コーナー自体がfirstandのさまざまな置換を使用して定義できるためです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)       |
+---------------+-------------------+-------------+

このため、外側の4つのコーナーから回転を開始します。最初に回転させます。でハイライトしましょう*

* 1 *
3 4 5
* 7 *

それぞれ**右側のと入れ替えたいと思います。では、firstandのさまざまな順列のみを使用して定義されたコーナーを印刷してみましょう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]

すごい!マトリックスの各コーナーの回転に成功しました。ただし、各レイヤーの中央で要素を回転させていません。明らかに、レイヤー内で反復する方法が必要です。

問題は、これまでのところ、関数内の唯一のループ(レイヤーループ)で、反復ごとに次のレイヤーに移動することです。マトリックスには回転可能なレイヤーが1つしかないため、レイヤーループはコーナーのみを回転した後に終了します。より大きな5×5マトリックス(2つのレイヤーが回転する必要がある場合)で何が起こるかを見てみましょう。関数コードは省略されていますが、上記と同じです。

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変数に加えて、レイヤー内を移動するためのオフセットを使用します。この新しいループを「要素ループ」と呼びます。要素ループは、上の行にある各要素、右側の下にある各要素、下の行にある各要素、および左側にある各要素を訪問します。

  • 一番上の行に沿って前方に移動するには、列のインデックスをインクリメントする必要があります。
  • 右側を下に移動するには、行インデックスをインクリメントする必要があります。
  • 下部に沿って後方に移動するには、列インデックスをデクリメントする必要があります。
  • 左側を上に移動するには、行インデックスをデクリメントする必要があります。

これは複雑に聞こえますが、上記を達成するためにインクリメントおよびデクリメントする回数は、マトリックスの4つの辺すべてで同じままなので、簡単になります。例えば:

  • 一番上の行に1つの要素を移動します。
  • 右に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  

最初は「すごい、これまでで最高の説明」のように感じましたが、何度か読んだ後(言葉の海で重要なものを見逃さないように)、私の意見は「男、わかりました、できる動かし続けてください?」そのような手の込んだ答えを作成するのに何時間もかかっていたはずであるものを取るためにまだ賛成です。
Abhijit Sarkar

1
@AbhijitSarkar-賛成票をいただきありがとうございます。少なくとも少しはお役に立てば幸いです。もちろん、そうです、私の答えは冗長です。ただし、これは回答の大多数とは対照的に意図的に行われたものです。私の回答の冒頭で述べたように、「この回答では、主要な概念が繰り返され、ペースは遅く、意図的に繰り返されています。」 明確さと必要な反復性を維持しながら、単語数を減らした編集がある場合、私は提案に非常にオープンです。または、単に編集します:)
Jack

@jack本当に良い説明。しかし、理解できませんでした。オフセット=要素-最初と最後=サイズ-最初-1はどのようにして思いついたのですか。これを理解するのに苦労しましたか?また、最終オフセットはオフセットと同じですか?
ashishjmeshram

1
TL; DR:list(zip(*reversed(your_list_of_lists)))
ボリス

127

Python:

rotated = list(zip(*original[::-1]))

反時計回り:

rotated_ccw = list(zip(*original))[::-1]

仕組み:

zip(*original)リストの対応するアイテムを新しいリストにスタックすることにより、2D配列の軸を交換します。(*演算子は、含まれているリストを引数に分散するように関数に指示します)

>>> list(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]]

最後に、2つを組み合わせると回転変換になります。

配置の変更[::-1]により、マトリックスの異なるレベルのリストが逆になります。


3
このコードは、Peter Norvigに由来するものだと思います。norvig.com
Josip

zip(*reversed(original))代わりにzip(*original[::-1])を使用して、元のリストの余分なコピーを作成しないようにすることができます。
ボリス

70

これは、結果を保持するために完全に新しい配列を使用する代わりに、所定の位置で回転を行うものです。配列の初期化と出力は省略しました。これは正方形の配列でのみ機能しますが、任意のサイズにすることができます。メモリのオーバーヘッドは、配列の1つの要素のサイズに等しいので、必要なだけ配列をローテーションできます。

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;
    }
}

少なくとも1つのバグを確認できます。コードを投稿する場合は、コードをテストするか、少なくとも行っていないことを伝えます。
ヒューアレン

1
どこ?それを指摘し、私はそれを修正します。私はそれをテストしました、そしてそれは奇数と偶数のサイズの配列の両方でうまくいきました。
dagorym 2008年

2
その美しいソリューション。目的に設定されている場合、心はそのような偉業を実行できます。O(n2)からO(1)へ
MoveFast 2012

2
O(1)ではありません。まだO(n ^ 2)です
duma

11
そのO(n ^ 2)とメモリO(1)。
Neel

38

ここには優れたコードがたくさんありますが、コードロジックをもう少しよく理解できるように、幾何学的に何が行われているのかを示したいと思います。これが私がこれに取り組む方法です。

まず、これを非常に簡単な転置と混同しないでください。

基本的な考え方は、それをレイヤーとして扱い、一度に1つのレイヤーを回転させることです。

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

これで外縁が処理されるので、基本的には一度に1シェルずつ

最後に真ん中の四角(または、奇数の場合は、移動しない最後の要素のみ)

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]

「エッジの途中」とはどういう意味ですか?多くのアルゴリズムはN / 2までルー​​プし、他のアルゴリズムはNまでループしますが、N / 2がどこから来ているのかわかりません。
PDN 2016年

私はそれがコーディングのインタビューを解読することで与えられたのと同じ解決策を信じています。しかし、私は段階的な説明が好きです。とても素敵で徹底的です。
Naphstor 2016年

@PDN この回答はそれを詳細に説明しています。
Mathias Bynens 2017

35

以前の投稿で述べたように、任意のサイズの行列に対してO(1)行列の回転を実装するC#のコードを次に示します。簡潔さと読みやすさのために、エラーチェックや範囲チェックはありません。コード:

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;
}

OK、手を上げます。回転しても、元の配列は実際には変更されません。ただし、オブジェクトがクラスのクライアントにローテーションされているように見える限り、オブジェクトシステムでは問題ありません。現時点では、Matrixクラスは元の配列データへの参照を使用しているため、m1の値を変更すると、m2とm3も変更されます。コンストラクタを少し変更して新しい配列を作成し、その配列に値をコピーすると、その配列が整理されます。


4
ブラボー!これは非常に優れた解決策であり、なぜそれが受け入れられた答えではないのかわかりません。
martinatime 2008

@martinatime:おそらく5倍の大きさのため
Toad

@Toad:スピード、サイズ、コスト、など:まあ、コードを書くことは、常に競合する要件との間のトレードオフである
Skizz

15
真...別の問題は、マトリックスが実際には回転されず、「ジャストインタイム」で回転されるという事実です。これは、いくつかの要素にアクセスするのに最適ですが、このマトリックスが計算や画像操作で使用された場合は恐ろしいでしょう。つまり、O(1)と言っても、それほど公平ではありません。
ヒキガエル

23

データを適切に回転させる必要がある場合(物理的に格納された表現を更新する場合など)は、配列アクセスに間接層を追加すると、おそらくインターフェイスが単純になり、パフォーマンスが向上します。

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)呼び出しに置き換えられました。これは仮想メソッド呼び出しであり、配列への直接アクセスよりも遅いため、回転後に回転した配列が使用される頻度に依存します。一度使用すると、このアプローチが確実に成功します。ローテーションしてから長期間実行するシステムで数日間使用すると、インプレースローテーションのパフォーマンスが向上する可能性があります。また、初期費用を受け入れることができるかどうかにも依存します。

すべてのパフォーマンスの問題と同様に、測定、測定、測定!


1
+1 ...そして、マトリックスが非常に大きく、2つの要素(スパース使用)のみにアクセスする場合、さらに効果的です
lothar

16
これをO(1)時間解と呼ぶのは少し不公平に思えます。OPによって引き起こされる問題を解決するには、O(n ^ 2)時間かかります。それだけでなく、transposeを返すため、問題は解決しません。与えられた例では、解法として転置がありません。
Vlad the Impala

5
ここで、マトリックスの最初の3つの要素だけが必要な場合、これはすばらしい解決策ですが、問題は完全に変換されたマトリックスを取得することです(つまり、すべてのマトリックス要素が必要であると想定)。これをO(1)と呼ぶのは、アルゴリズム分析のクレジットデフォルトスワップメソッドです。問題は解決していません。他の誰かにプッシュしました:)
Ana Betts

4
@Paul Betts:私はあなたの要点を理解しますが、上記のコメントで書いたように、実際に転置された行列がある場合でも、値を読み取るにはループを記述する必要があります。したがって、行列からすべての値を読み取ることは、常にO(N ^ 2)です。ここでの違いは、転置、回転、拡大縮小、再拡大縮小などを行った場合でも、O(N ^ 2)ヒットを1回しか受け取らないことです。私が言ったように、これは常に最良の解決策ではありませんが、多くの場合、それは適切で価値があります。OPは魔法の解決策を探しているようで、これはあなたが得るのと同じくらい近いものです。
Drew Noakes、2010年

9
この答えは好きですが、指摘したいことがあります。デコレートされたマトリックスの出力(および一般的に他の順次読み取りの実行)は、メモリ内でローテーションされたマトリックスに対して同じことを行うよりもはるかに遅くなる可能性があります。これは、仮想メソッドの呼び出しだけが原因ではありません。大きなマトリックスの場合、「全体」ではなく「下」を読み取ることで、キャッシュミスの数を大幅に増やすことになります。
マイクダニエルズ

18

これはJavaでのより良いバージョンです:幅と高さが異なるマトリックス用に作成しました

  • ここでhは回転後の行列の高さです
  • ここでwは回転後の行列の幅です

 

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;
}

このコードは、Nick Berardiの投稿に基づいています。


ありがとう。これは、ここで最も明確なJavaコードでした。質問-あなた/ニックはどのように[w-j-1]の部分を思いついたのですか?@tweakingの回答を見ると、誘導/解決の例からどのようにそれを導き出すことができるかがわかります。それがそれをどのようにして得たのか、それとも行列に関連するいくつかの数学的原理に基づいているのかと思っているだけです。
クエストモンガー2015

17

ルビーウェイ: .transpose.map &:reverse


1
それよりもさらに単純です。array.reverse.transpose配列を時計回りにarray.transpose.reverse回転させながら反時計回りに回転させます。の必要はありませんmap
Giorgi Gzirishvili

13

すでに多くの回答があり、O(1)時間の複雑さを主張する2つを見つけました。本物の O(1)アルゴリズムはそのままアレイストレージを残すことで、変化をどのようにインデックス、その要素。ここでの目標は、追加のメモリを消費せず、データを反復するために追加の時間を必要としないことです。

90度、-90度、180度の回転は単純な変換であり、2D配列の行と列の数がわかっていれば実行できます。ベクトルを90度回転するには、軸を入れ替えてY軸を無効にします。-90度の場合、軸を交換してX軸を無効にします。180度の場合、スワップせずに両方の軸を無効にします。

軸を個別に無効にすることにより、水平方向および/または垂直方向にミラーリングするなど、さらなる変換が可能です。

これは、たとえば、アクセサメソッドを介して実行できます。以下の例はJavaScript関数ですが、概念はすべての言語に等しく適用されます。

 // Get an array element in column/row order
 var getArray2d = function(a, x, y) {
   return a[y][x];
 };

 //demo
 var arr = [
   [5, 4, 6],
   [1, 7, 9],
   [-2, 11, 0],
   [8, 21, -3],
   [3, -1, 2]
 ];

 var newarr = [];
 arr[0].forEach(() => newarr.push(new Array(arr.length)));

 for (var i = 0; i < newarr.length; i++) {
   for (var j = 0; j < newarr[0].length; j++) {
     newarr[i][j] = getArray2d(arr, i, j);
   }
 }
 console.log(newarr);

// Get an array element rotated 90 degrees clockwise
function getArray2dCW(a, x, y) {
  var t = x;
  x = y;
  y = a.length - t - 1;
  return a[y][x];
}

//demo
var arr = [
  [5, 4, 6],
  [1, 7, 9],
  [-2, 11, 0],
  [8, 21, -3],
  [3, -1, 2]
];

var newarr = [];
arr[0].forEach(() => newarr.push(new Array(arr.length)));

for (var i = 0; i < newarr[0].length; i++) {
  for (var j = 0; j < newarr.length; j++) {
    newarr[j][i] = getArray2dCW(arr, i, j);
  }
}
console.log(newarr);

// Get an array element rotated 90 degrees counter-clockwise
function getArray2dCCW(a, x, y) {
  var t = x;
  x = a[0].length - y - 1;
  y = t;
  return a[y][x];
}

//demo
var arr = [
  [5, 4, 6],
  [1, 7, 9],
  [-2, 11, 0],
  [8, 21, -3],
  [3, -1, 2]
];

var newarr = [];
arr[0].forEach(() => newarr.push(new Array(arr.length)));

for (var i = 0; i < newarr[0].length; i++) {
  for (var j = 0; j < newarr.length; j++) {
    newarr[j][i] = getArray2dCCW(arr, i, j);
  }
}
console.log(newarr);

// Get an array element rotated 180 degrees
function getArray2d180(a, x, y) {
  x = a[0].length - x - 1;
  y = a.length - y - 1;
  return a[y][x];
}

//demo
var arr = [
  [5, 4, 6],
  [1, 7, 9],
  [-2, 11, 0],
  [8, 21, -3],
  [3, -1, 2]
];

var newarr = [];
arr.forEach(() => newarr.push(new Array(arr[0].length)));

for (var i = 0; i < newarr[0].length; i++) {
  for (var j = 0; j < newarr.length; j++) {
    newarr[j][i] = getArray2d180(arr, i, j);
  }
}
console.log(newarr);

このコードは、各内部配列が行である入れ子配列の配列を想定しています。

このメソッドを使用すると、配列が回転または変換されたかのように要素を(ランダムな順序で)読み取る(または書き込む)ことができます。おそらく参照により、呼び出す適切な関数を選択するだけで、すぐに使用できます!

この概念を拡張して、アクセサメソッドを通じて変換を追加的(および非破壊的)に適用できます。任意の角度回転とスケーリングを含みます。


ただし、これらは実際には元の配列から回転していません。1つ目は、最終結果が単純に転置されることです。2つ目は、行をシャッフルしたか、水平方向の中央でミラーリングしたように見えます。3番目は、行を反転しただけで、4番目も転置されます。実際に「回転」したものはありません。
SM177Y 2018

後の2つの例にはいくつかのバグがあります。修正するのは簡単です。この解決策はインプレースローテーションではないことを明示的に指摘しました。これは変換関数であり、遅延反復に適しています。
Jason Oster

ローテーションがないことを除いて、OPが要求した内容に実際には応答しませんでした。
SM177Y

@ SM177Y別の編集者が、機能しないサンプルコードを私の回答に追加しました。あなたはそれによってどのように混乱したのかわかります。反復ループのバグを修正しました。提供される関数は、実際には配列内のデータを「回転」させます。
Jason Oster

また、重要な詳細は、コード例が私が提供した元の答えを本当に洗い流しており、線形時空複雑性ソリューションに対する関数変換の力を説明しようとしていました。関数変換を使用すると、既に配列要素を反復処理またはアクセスしているため、変換は一定の空間と時間の複雑さという意味で「無料」と見なされます。
Jason Oster

10

数人の人々がすでに新しいアレイを作ることを含む例を上げています。

考慮すべき他のいくつかのこと:

(a)実際にデータを移動する代わりに、単に「回転した」配列を異なる方法でトラバースします。

(b)インプレースでローテーションを行うと、少しトリッキーになる場合があります。少しスクラッチの場所が必要になります(おそらく1行または1列のサイズとほぼ同じです)。インプレース転置の実行に関する古くからのACM論文(http://doi.acm.org/10.1145/355719.355729)がありますが、それらのサンプルコードは厄介なgotoを含むFORTRANです。

補遺:

http://doi.acm.org/10.1145/355611.355612は、別の、おそらく優れたインプレース転置アルゴリズムです。


これに同意する。ソースデータと「ローテーションされた」データとの間の変換を決定するメソッドを用意します。
martinatime 2008

8

Nickの答えは、(NxNとは対照的に)わずかな変更のみでNxM配列にも機能します。

string[,] orig = new string[n, m];
string[,] rot = new string[m, n];

...

for ( int i=0; i < n; i++ )
  for ( int j=0; j < m; j++ )
    rot[j, n - i - 1] = orig[i, j];

これについて考える1つの方法は、軸の中心(0,0)を左上隅から右上隅に移動したことです。あなたは単に一方から他方へ移調しているだけです。


6

時間-O(N)、スペース-O(1)

public void rotate(int[][] matrix) {
    int n = matrix.length;
    for (int i = 0; i < n / 2; i++) {
        int last = n - 1 - i;
        for (int j = i; j < last; j++) {
            int top = matrix[i][j];
            matrix[i][j] = matrix[last - j][i];
            matrix[last - j][i] = matrix[last][last - j];
            matrix[last][last - j] = matrix[j][last];
            matrix[j][last] = top;
        }
    }
}

これはO(1)ではありません。これはO(n)です。
Jason Oster 2014年

@JasonOster追加のスペースを消費しないため、これはO(1)スペースであると思います。
2014年

私の過ち。O(1)スペースの複雑さ、はい。O(n)時間の複雑さ。
Jason Oster 2014年

空間の複雑さもO(n)です。スペースの複雑さには、入力変数サイズのスペースを含める必要があります。carriercup.com/question?id=14952322
Jason Heo

反時計回りに機能するようにこれを変更するにはどうすればよいですか?
MD XF

5

これが私のRubyバージョンです(値は同じようには表示されませんが、説明どおりに回転します)。

def rotate(matrix)
  result = []
  4.times { |x|
    result[x] = []
    4.times { |y|
      result[x][y] = matrix[y][3 - x]
    }
  }

  result
end

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

def print_matrix(matrix)
  4.times { |y|
    4.times { |x|
      print "#{matrix[x][y]} "
    }
    puts ""
  }
end

print_matrix(matrix)
puts ""
print_matrix(rotate(matrix))

出力:

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

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

4

これが正方形専用のjavaによる空間内回転メソッドです。正方形でない2D配列の場合は、とにかく新しい配列を作成する必要があります。

private void rotateInSpace(int[][] arr) {
    int z = arr.length;
    for (int i = 0; i < z / 2; i++) {
        for (int j = 0; j < (z / 2 + z % 2); j++) {
            int x = i, y = j;
            int temp = arr[x][y];
            for (int k = 0; k < 4; k++) {
                int temptemp = arr[y][z - x - 1];
                arr[y][z - x - 1] = temp;
                temp = temptemp;

                int tempX = y;
                y = z - x - 1;
                x = tempX;
            }
        }
    }
}

新しい配列を作成して任意のサイズの2D配列を回転させるコード:

private int[][] rotate(int[][] arr) {
    int width = arr[0].length;
    int depth = arr.length;
    int[][] re = new int[width][depth];
    for (int i = 0; i < depth; i++) {
        for (int j = 0; j < width; j++) {
            re[j][depth - i - 1] = arr[i][j];
        }
    }
    return re;
}

3

JavaScriptでのディンプルの+90擬似コードの実装(例:転置してから各行を反転):

function rotate90(a){
  // transpose from http://www.codesuck.com/2012/02/transpose-javascript-array-in-one-line.html
  a = Object.keys(a[0]).map(function (c) { return a.map(function (r) { return r[c]; }); });
  // row reverse
  for (i in a){
    a[i] = a[i].reverse();
  }
  return a;
}

3

これは3つの簡単なステップで実行できます

1)行列があるとします

   1 2 3
   4 5 6
   7 8 9

2)行列の転置を行う

   1 4 7
   2 5 8
   3 6 9

3)行を交換して回転した行列を取得する

   3 6 9
   2 5 8
   1 4 7

このための Javaソースコード

public class MyClass {

    public static void main(String args[]) {
        Demo obj = new Demo();
        /*initial matrix to rotate*/
        int[][] matrix = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
        int[][] transpose = new int[3][3]; // matrix to store transpose

        obj.display(matrix);              // initial matrix

        obj.rotate(matrix, transpose);    // call rotate method
        System.out.println();
        obj.display(transpose);           // display the rotated matix
    }
}

class Demo {   
    public void rotate(int[][] mat, int[][] tran) {

        /* First take the transpose of the matrix */
        for (int i = 0; i < mat.length; i++) {
            for (int j = 0; j < mat.length; j++) {
                tran[i][j] = mat[j][i]; 
            }
        }

        /*
         * Interchange the rows of the transpose matrix to get rotated
         * matrix
         */
        for (int i = 0, j = tran.length - 1; i != j; i++, j--) {
            for (int k = 0; k < tran.length; k++) {
                swap(i, k, j, k, tran);
            }
        }
    }

    public void swap(int a, int b, int c, int d, int[][] arr) {
        int temp = arr[a][b];
        arr[a][b] = arr[c][d];
        arr[c][d] = temp;    
    }

    /* Method to display the matrix */
    public void display(int[][] arr) {
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length; j++) {
                System.out.print(arr[i][j] + " ");
            }
            System.out.println();
        }
    }
}

出力:

1 2 3 
4 5 6 
7 8 9 

3 6 9 
2 5 8 
1 4 7 

2

これはC、O(1)のメモリの複雑さで、時計回りに90度回転させた私の実装です。

#include <stdio.h>

#define M_SIZE 5

static void initMatrix();
static void printMatrix();
static void rotateMatrix();

static int m[M_SIZE][M_SIZE];

int main(void){
    initMatrix();
    printMatrix();
    rotateMatrix();
    printMatrix();

    return 0;
}

static void initMatrix(){
    int i, j;

    for(i = 0; i < M_SIZE; i++){
        for(j = 0; j < M_SIZE; j++){
            m[i][j] = M_SIZE*i + j + 1;
        }
    }
}

static void printMatrix(){
    int i, j;

    printf("Matrix\n");
    for(i = 0; i < M_SIZE; i++){
        for(j = 0; j < M_SIZE; j++){
            printf("%02d ", m[i][j]);
        }
        printf("\n");
    }
    printf("\n");
}

static void rotateMatrix(){
    int r, c;

    for(r = 0; r < M_SIZE/2; r++){
        for(c = r; c < M_SIZE - r - 1; c++){
            int tmp = m[r][c];

            m[r][c] = m[M_SIZE - c - 1][r];
            m[M_SIZE - c - 1][r] = m[M_SIZE - r - 1][M_SIZE - c - 1];
            m[M_SIZE - r - 1][M_SIZE - c - 1] = m[c][M_SIZE - r - 1];
            m[c][M_SIZE - r - 1] = tmp;
        }
    }
}

2

Javaのバージョンは次のとおりです。

public static void rightRotate(int[][] matrix, int n) {
    for (int layer = 0; layer < n / 2; layer++) {
        int first = layer;
        int last = n - 1 - first;
        for (int i = first; i < last; i++) {
           int offset = i - first;
           int temp = matrix[first][i];
           matrix[first][i] = matrix[last-offset][first];
           matrix[last-offset][first] = matrix[last][last-offset];
           matrix[last][last-offset] = matrix[i][last];
           matrix[i][last] = temp;
        }
    }
}

この方法では、最初に最も外側のレイヤーを回転させ、次に順次内側のレイヤーに移動します。


2

線形の観点から、行列を考えます:

    1 2 3        0 0 1
A = 4 5 6    B = 0 1 0
    7 8 9        1 0 0

今転置してください

     1 4 7
A' = 2 5 8
     3 6 9

そして、Bに対するA 'の作用、またはA'に対するBの作用を検討してください。
それぞれ:

      7 4 1          3 6 9
A'B = 8 5 2    BA' = 2 5 8
      9 6 3          1 4 7

これは、どのnxnマトリックスでも拡張できます。そして、この概念をコードにすばやく適用します:

void swapInSpace(int** mat, int r1, int c1, int r2, int c2)
{
    mat[r1][c1] ^= mat[r2][c2];
    mat[r2][c2] ^= mat[r1][c1];
    mat[r1][c1] ^= mat[r2][c2];
}

void transpose(int** mat, int size)
{
    for (int i = 0; i < size; i++)
    {
        for (int j = (i + 1); j < size; j++)
        {
            swapInSpace(mat, i, j, j, i);
        }
    }
}

void rotate(int** mat, int size)
{
    //Get transpose
    transpose(mat, size);

    //Swap columns
    for (int i = 0; i < size / 2; i++)
    {
        for (int j = 0; j < size; j++)
        {
            swapInSpace(mat, i, j, size - (i + 1), j);
        }
    }
}

2

[n、m] 2D配列を右に90度回転するC#コード

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MatrixProject
{
    // mattrix class

    class Matrix{
        private int rows;
        private int cols;
        private int[,] matrix;

        public Matrix(int n){
            this.rows = n;
            this.cols = n;
            this.matrix = new int[this.rows,this.cols];

        }

        public Matrix(int n,int m){
            this.rows = n;
            this.cols = m;

            this.matrix = new int[this.rows,this.cols];
        }

        public void Show()
        {
            for (var i = 0; i < this.rows; i++)
            {
                for (var j = 0; j < this.cols; j++) {
                    Console.Write("{0,3}", this.matrix[i, j]);
                }
                Console.WriteLine();
            }                
        }

        public void ReadElements()
        {
           for (var i = 0; i < this.rows; i++)
                for (var j = 0; j < this.cols; j++)
                {
                    Console.Write("element[{0},{1}]=",i,j);
                    this.matrix[i, j] = Convert.ToInt32(Console.ReadLine());
                }            
        }


        // rotate [n,m] 2D array by 90 deg right
        public void Rotate90DegRight()
        {

            // create a mirror of current matrix
            int[,] mirror = this.matrix;

            // create a new matrix
            this.matrix = new int[this.cols, this.rows];

            for (int i = 0; i < this.rows; i++)
            {
                for (int j = 0; j < this.cols; j++)
                {
                    this.matrix[j, this.rows - i - 1] = mirror[i, j];
                }
            }

            // replace cols count with rows count
            int tmp = this.rows;
            this.rows = this.cols;
            this.cols = tmp;           
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Matrix myMatrix = new Matrix(3,4);
            Console.WriteLine("Enter matrix elements:");
            myMatrix.ReadElements();
            Console.WriteLine("Matrix elements are:");
            myMatrix.Show();
            myMatrix.Rotate90DegRight();
            Console.WriteLine("Matrix rotated at 90 deg are:");
            myMatrix.Show();
            Console.ReadLine();
        }
    }
}

結果:

    Enter matrix elements:
    element[0,0]=1
    element[0,1]=2
    element[0,2]=3
    element[0,3]=4
    element[1,0]=5
    element[1,1]=6
    element[1,2]=7
    element[1,3]=8
    element[2,0]=9
    element[2,1]=10
    element[2,2]=11
    element[2,3]=12
    Matrix elements are:
      1  2  3  4
      5  6  7  8
      9 10 11 12
    Matrix rotated at 90 deg are:
      9  5  1
     10  6  2
     11  7  3
     12  8  4

2

PHP:

<?php    
$a = array(array(1,2,3,4),array(5,6,7,8),array(9,0,1,2),array(3,4,5,6));
$b = array(); //result

while(count($a)>0)
{
    $b[count($a[0])-1][] = array_shift($a[0]);
    if (count($a[0])==0)
    {
         array_shift($a);
    }
}

PHP5.6から、sleak呼び出しで配列の転置を実行できますarray_map()。つまり、列は行に変換されます。

コード:(デモ

$array = [
    [1, 2, 3, 4],
    [5, 6, 7, 8],
    [9, 0, 1, 2],
    [3, 4, 5, 6]
];
$transposed = array_map(null, ...$array);

$ transposed:

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

1

For i:= 0 to X do For j := 0 to X do graphic[j][i] := graphic2[X-i][j]

Xはグラフィックが入っている配列のサイズです。


1

#transposeは、RubyのArrayクラスの標準メソッドであるため、次のようになります。

% irb
irb(main):001:0> m = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 0, 1, 2], [3, 4, 5, 6]]
=> [[1, 2, 3, 4], [5, 6, 7, 8], [9, 0, 1, 2], [3, 4, 5, 6]] 
irb(main):002:0> m.reverse.transpose
=> [[3, 9, 5, 1], [4, 0, 6, 2], [5, 1, 7, 3], [6, 2, 8, 4]]

実装は、Cで記述されたn ^ 2転置関数です。http//www.ruby-doc.org/core-1.9.3/Array.html#method-i-transposeで確認でき ます。 「転置」の横にある「ソースを切り替える」。

私はO(n ^ 2)解よりもよく覚えていますが、特別に構成された行列(スパース行列など)の場合のみです


1

行列を時計回りに90度回転するためのCコードは、任意のM * N行列に対してIN PLACE

void rotateInPlace(int * arr[size][size], int row, int column){
    int i, j;
    int temp = row>column?row:column;
    int flipTill = row < column ? row : column;
    for(i=0;i<flipTill;i++){
        for(j=0;j<i;j++){
            swapArrayElements(arr, i, j);
        }
    }

    temp = j+1;

    for(i = row>column?i:0; i<row; i++){
            for(j=row<column?temp:0; j<column; j++){
                swapArrayElements(arr, i, j);
            }
    }

    for(i=0;i<column;i++){
        for(j=0;j<row/2;j++){
            temp = arr[i][j];
            arr[i][j] = arr[i][row-j-1];
            arr[i][row-j-1] = temp;
        }
    }
}

1

ここに私のCのインプレース実装があります

void rotateRight(int matrix[][SIZE], int length) {

    int layer = 0;

    for (int layer = 0; layer < length / 2; ++layer) {

        int first = layer;
        int last = length - 1 - layer;

        for (int i = first; i < last; ++i) {

            int topline = matrix[first][i];
            int rightcol = matrix[i][last];
            int bottomline = matrix[last][length - layer - 1 - i];
            int leftcol = matrix[length - layer - 1 - i][first];

            matrix[first][i] = leftcol;
            matrix[i][last] = topline;
            matrix[last][length - layer - 1 - i] = rightcol;
            matrix[length - layer - 1 - i][first] = bottomline;
        }
    }
}

1

これは、Cでの2ステップの解決策である行列90度回転の私の試みです。最初に行列を所定の位置に転置し、次にcolsを交換します。

#define ROWS        5
#define COLS        5

void print_matrix_b(int B[][COLS], int rows, int cols) 
{
    for (int i = 0; i <= rows; i++) {
        for (int j = 0; j <=cols; j++) {
            printf("%d ", B[i][j]);
        }
        printf("\n");
    }
}

void swap_columns(int B[][COLS], int l, int r, int rows)
{
    int tmp;
    for (int i = 0; i <= rows; i++) {
        tmp = B[i][l];
        B[i][l] = B[i][r];
        B[i][r] = tmp;
    }
}


void matrix_2d_rotation(int B[][COLS], int rows, int cols)
{
    int tmp;
    // Transpose the matrix first
    for (int i = 0; i <= rows; i++) {
        for (int j = i; j <=cols; j++) {
            tmp = B[i][j];
            B[i][j] = B[j][i];
            B[j][i] = tmp;
        }
    }
    // Swap the first and last col and continue until
    // the middle.
    for (int i = 0; i < (cols / 2); i++)
        swap_columns(B, i, cols - i, rows);
}



int _tmain(int argc, _TCHAR* argv[])
{
    int B[ROWS][COLS] = { 
                  {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}
                        };

    matrix_2d_rotation(B, ROWS - 1, COLS - 1);

    print_matrix_b(B, ROWS - 1, COLS -1);
    return 0;
}

1

@dagorym:ああ、男。「退屈だ、何を考えればいいか」という良いパズルとして、私はこれにぶら下がっていました。私はインプレース転置コードを思いつきましたが、あなたのコードが私のものとほとんど同じであることを確認するためにここに来ました...まあ。こちらはRubyです。

require 'pp'
n = 10
a = []
n.times { a << (1..n).to_a }

pp a

0.upto(n/2-1) do |i|
  i.upto(n-i-2) do |j|
    tmp             = a[i][j]
    a[i][j]         = a[n-j-1][i]
    a[n-j-1][i]     = a[n-i-1][n-j-1]
    a[n-i-1][n-j-1] = a[j][n-i-1]
    a[j][n-i-1]     = tmp
  end
end

pp a

1
short normal[4][4] = {{8,4,7,5},{3,4,5,7},{9,5,5,6},{3,3,3,3}};

short rotated[4][4];

for (int r = 0; r < 4; ++r)
{
  for (int c = 0; c < 4; ++c)
  {
    rotated[r][c] = normal[c][3-r];
  }
}

単純なC ++メソッド。大きな配列では大きなメモリオーバーヘッドが発生します。


これらすべての答えの中で、コンパクトで回転に十分なこの答えを見つけてテストしました
dlewin
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.