もう少し詳しく説明します。この回答では、主要な概念が繰り返され、ペースは遅く、意図的に繰り返されます。ここで提供されるソリューションは、構文的に最もコンパクトではありませんが、行列の回転とその結果の実装について知りたい人を対象としています。
まず、行列とは何ですか?この回答では、マトリックスは幅と高さが同じグリッドにすぎません。行列の幅と高さは異なる場合がありますが、簡単にするために、このチュートリアルでは幅と高さが等しい行列(正方行列)のみを考慮します。そして、はい、行列は行列の複数形です。
マトリックスの例は、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
これで、各レイヤーの行と列の位置を提供するループができました。変数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 | . . . . . |
+-----+-----------+
したがって、マトリックスのレイヤーをナビゲートできます。ここで、レイヤー内で要素を移動できるように、レイヤー内を移動する方法が必要です。要素があるレイヤーから別のレイヤーに「ジャンプ」することはありませんが、要素はそれぞれのレイヤー内で移動します。
レイヤー内の各要素を回転すると、レイヤー全体が回転します。マトリックス内のすべてのレイヤーを回転すると、マトリックス全体が回転します。この文章は非常に重要なので、先に進む前によく理解して理解してください。
ここで、実際に要素を移動する方法が必要です。つまり、各要素を回転させ、続いてレイヤー、そして最終的にはマトリックスを回転させます。簡単にするために、回転可能なレイヤーが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は、行列の四隅を参照するために簡単に使用できます。これは、コーナー自体がfirst
andのさまざまな置換を使用して定義できるためです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 *
それぞれ*
を*
右側のと入れ替えたいと思います。では、first
andのさまざまな順列のみを使用して定義されたコーナーを印刷してみましょう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