Ich möchte etwas mehr Details hinzufügen. In dieser Antwort werden Schlüsselkonzepte wiederholt, das Tempo ist langsam und wiederholt sich absichtlich. Die hier bereitgestellte Lösung ist nicht die syntaktisch kompakteste, sie ist jedoch für diejenigen gedacht, die lernen möchten, was Matrixrotation und die daraus resultierende Implementierung sind.
Was ist eine Matrix? Für die Zwecke dieser Antwort ist eine Matrix nur ein Raster, in dem Breite und Höhe gleich sind. Beachten Sie, dass Breite und Höhe einer Matrix unterschiedlich sein können. Der Einfachheit halber werden in diesem Lernprogramm nur Matrizen mit gleicher Breite und Höhe berücksichtigt ( quadratische Matrizen ) . Und ja, Matrizen sind der Plural der Matrix.
Beispielmatrizen sind: 2 × 2, 3 × 3 oder 5 × 5. Oder allgemeiner N × N. Eine 2 × 2-Matrix hat 4 Quadrate, weil 2 × 2 = 4. Eine 5 × 5-Matrix hat 25 Quadrate, weil 5 × 5 = 25. Jedes Quadrat wird als Element oder Eintrag bezeichnet. Wir werden jedes Element mit einem Punkt ( .
) in den folgenden Diagrammen darstellen:
2 × 2 Matrix
. .
. .
3 × 3 Matrix
. . .
. . .
. . .
4 × 4 Matrix
. . . .
. . . .
. . . .
. . . .
Was bedeutet es also, eine Matrix zu drehen? Nehmen wir eine 2 × 2-Matrix und geben einige Zahlen in jedes Element ein, damit die Drehung beobachtet werden kann:
0 1
2 3
Wenn wir dies um 90 Grad drehen, erhalten wir:
2 0
3 1
Wir haben die gesamte Matrix buchstäblich einmal nach rechts gedreht, genau wie das Lenkrad eines Autos. Es kann hilfreich sein, die Matrix auf die rechte Seite zu „kippen“. Wir wollen in Python eine Funktion schreiben, die eine Matrix nimmt und sich einmal nach rechts dreht. Die Funktionssignatur lautet:
def rotate(matrix):
# Algorithm goes here.
Die Matrix wird mithilfe eines zweidimensionalen Arrays definiert:
matrix = [
[0,1],
[2,3]
]
Daher greift die erste Indexposition auf die Zeile zu. Die zweite Indexposition greift auf die Spalte zu:
matrix[row][column]
Wir definieren eine Utility-Funktion zum Drucken einer Matrix.
def print_matrix(matrix):
for row in matrix:
print row
Eine Methode zum Drehen einer Matrix besteht darin, jeweils eine Schicht zu erstellen. Aber was ist eine Schicht? Denken Sie an eine Zwiebel. Genau wie die Schichten einer Zwiebel bewegen wir uns beim Entfernen jeder Schicht in Richtung Zentrum. Andere Analogien ist a Matroschka-Puppe oder eine Partie Pass-the-Parcel.
Die Breite und Höhe einer Matrix bestimmen die Anzahl der Schichten in dieser Matrix. Verwenden wir für jede Ebene unterschiedliche Symbole:
Eine 2 × 2-Matrix hat 1 Schicht
. .
. .
Eine 3 × 3-Matrix hat 2 Schichten
. . .
. x .
. . .
Eine 4 × 4-Matrix hat 2 Schichten
. . . .
. x x .
. x x .
. . . .
Eine 5 × 5-Matrix hat 3 Schichten
. . . . .
. x x x .
. x O x .
. x x x .
. . . . .
Eine 6 × 6-Matrix hat 3 Schichten
. . . . . .
. x x x x .
. x O O x .
. x O O x .
. x x x x .
. . . . . .
Eine 7 × 7-Matrix hat 4 Schichten
. . . . . . .
. x x x x x .
. x O O O x .
. x O - O x .
. x O O O x .
. x x x x x .
. . . . . . .
Möglicherweise stellen Sie fest, dass das Erhöhen der Breite und Höhe einer Matrix um eins nicht immer die Anzahl der Ebenen erhöht. Wenn wir die obigen Matrizen nehmen und die Ebenen und Dimensionen tabellieren, sehen wir, dass die Anzahl der Ebenen alle zwei Inkremente von Breite und Höhe einmal zunimmt:
+-----+--------+
| N×N | Layers |
+-----+--------+
| 1×1 | 1 |
| 2×2 | 1 |
| 3×3 | 2 |
| 4×4 | 2 |
| 5×5 | 3 |
| 6×6 | 3 |
| 7×7 | 4 |
+-----+--------+
Es müssen jedoch nicht alle Schichten gedreht werden. Eine 1 × 1-Matrix ist vor und nach der Drehung dieselbe. Die zentrale 1 × 1-Schicht ist vor und nach der Drehung immer gleich, egal wie groß die Gesamtmatrix ist:
+-----+--------+------------------+
| 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 |
+-----+--------+------------------+
Wie können wir angesichts der N × N-Matrix programmgesteuert die Anzahl der Schichten bestimmen, die wir drehen müssen? Wenn wir die Breite oder Höhe durch zwei teilen und den Rest ignorieren, erhalten wir die folgenden Ergebnisse.
+-----+--------+------------------+---------+
| 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 |
+-----+--------+------------------+---------+
Beachte wie N/2
die Anzahl der Ebenen übereinstimmt, die gedreht werden müssen. Manchmal ist die Anzahl der drehbaren Schichten eins weniger als die Gesamtzahl der Schichten in der Matrix. Dies tritt auf, wenn die innerste Schicht nur aus einem Element (dh einer 1 × 1-Matrix) besteht und daher nicht gedreht werden muss. Es wird einfach ignoriert.
Wir werden diese Informationen zweifellos in unserer Funktion benötigen, um eine Matrix zu drehen. Fügen wir sie jetzt hinzu:
def rotate(matrix):
size = len(matrix)
# Rotatable layers only.
layer_count = size / 2
Jetzt wissen wir, was Ebenen sind und wie wir die Anzahl der Ebenen bestimmen können, die tatsächlich gedreht werden müssen. Wie isolieren wir eine einzelne Ebene, damit wir sie drehen können? Zunächst untersuchen wir eine Matrix von der äußersten Schicht nach innen bis zur innersten Schicht. Eine 5 × 5-Matrix besteht aus insgesamt drei Schichten und zwei Schichten, die gedreht werden müssen:
. . . . .
. x x x .
. x O x .
. x x x .
. . . . .
Schauen wir uns zuerst die Spalten an. Die Position der Spalten, die die äußerste Schicht definieren, unter der Annahme, dass wir von 0 zählen, ist 0 und 4:
+--------+-----------+
| Column | 0 1 2 3 4 |
+--------+-----------+
| | . . . . . |
| | . x x x . |
| | . x O x . |
| | . x x x . |
| | . . . . . |
+--------+-----------+
0 und 4 sind auch die Positionen der Zeilen für die äußerste Schicht.
+-----+-----------+
| Row | |
+-----+-----------+
| 0 | . . . . . |
| 1 | . x x x . |
| 2 | . x O x . |
| 3 | . x x x . |
| 4 | . . . . . |
+-----+-----------+
Dies ist immer der Fall, da Breite und Höhe gleich sind. Daher können wir die Spalten- und Zeilenpositionen einer Ebene mit nur zwei Werten (anstatt vier) definieren.
Wenn Sie nach innen zur zweiten Ebene gehen, sind die Spalten 1 und 3. Und ja, Sie haben es erraten, es ist dasselbe für Zeilen. Es ist wichtig zu verstehen, dass wir die Zeilen- und Spaltenpositionen sowohl erhöhen als auch verringern mussten, wenn wir nach innen zur nächsten Ebene gingen.
+-----------+---------+---------+---------+
| Layer | Rows | Columns | Rotate? |
+-----------+---------+---------+---------+
| Outermost | 0 and 4 | 0 and 4 | Yes |
| Inner | 1 and 3 | 1 and 3 | Yes |
| Innermost | 2 | 2 | No |
+-----------+---------+---------+---------+
Um jede Ebene zu untersuchen, möchten wir eine Schleife mit sowohl zunehmenden als auch abnehmenden Zählern, die eine Bewegung nach innen darstellen, beginnend mit der äußersten Ebene. Wir nennen dies unsere "Layer-Schleife".
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)
Der obige Code durchläuft die Positionen (Zeilen und Spalten) aller Ebenen, die gedreht werden müssen.
Layer 0: first: 0, last: 4
Layer 1: first: 1, last: 3
Wir haben jetzt eine Schleife, die die Positionen der Zeilen und Spalten jeder Ebene angibt. Die Variablen first
und last
identifizieren die Indexposition der ersten und letzten Zeilen und Spalten. Zurück zu unseren Zeilen- und Spaltentabellen:
+--------+-----------+
| 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 | . . . . . |
+-----+-----------+
So können wir durch die Ebenen einer Matrix navigieren. Jetzt brauchen wir eine Möglichkeit, innerhalb einer Ebene zu navigieren, damit wir Elemente auf dieser Ebene verschieben können. Beachten Sie, dass Elemente niemals von einer Ebene zur anderen "springen", sondern sich innerhalb ihrer jeweiligen Ebenen bewegen.
Durch Drehen jedes Elements in einer Ebene wird die gesamte Ebene gedreht. Durch Drehen aller Ebenen in einer Matrix wird die gesamte Matrix gedreht. Dieser Satz ist sehr wichtig. Bitte versuchen Sie Ihr Bestes, um ihn zu verstehen, bevor Sie fortfahren.
Jetzt brauchen wir eine Möglichkeit, Elemente tatsächlich zu bewegen, dh jedes Element und anschließend die Ebene und letztendlich die Matrix zu drehen. Der Einfachheit halber kehren wir zu einer 3x3-Matrix zurück, die eine drehbare Ebene hat.
0 1 2
3 4 5
6 7 8
Unsere Ebenenschleife enthält die Indizes der ersten und letzten Spalte sowie der ersten und letzten Zeile:
+-----+-------+
| 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 |
+-----+-------+
Da unsere Matrizen immer quadratisch sind, benötigen wir nur zwei Variablen, first
und last
da die Indexpositionen für Zeilen und Spalten gleich sind.
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.
Die Variablen first und last können leicht verwendet werden, um auf die vier Ecken einer Matrix zu verweisen. Dies liegt daran, dass die Ecken selbst unter Verwendung verschiedener Permutationen von first
und last
(ohne Subtraktion, Addition oder Offset dieser Variablen) definiert werden können:
+---------------+-------------------+-------------+
| 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) |
+---------------+-------------------+-------------+
Aus diesem Grund beginnen wir unsere Drehung an den äußeren vier Ecken - wir drehen diese zuerst. Lassen Sie uns sie mit hervorheben *
.
* 1 *
3 4 5
* 7 *
Wir wollen jedes *
mit *
dem rechts davon tauschen . Drucken wir also unsere Ecken aus, die nur mit verschiedenen Permutationen von first
und definiert wurden 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)
Die Ausgabe sollte sein:
top_left: (0, 0)
top_right: (0, 2)
bottom_right: (2, 2)
bottom_left: (2, 0)
Jetzt können wir ganz einfach jede der Ecken innerhalb unserer Ebenenschleife vertauschen:
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 vor dem Drehen von Ecken:
[0, 1, 2]
[3, 4, 5]
[6, 7, 8]
Matrix nach rotierenden Ecken:
[6, 1, 0]
[3, 4, 5]
[8, 7, 2]
Toll! Wir haben jede Ecke der Matrix erfolgreich gedreht. Wir haben jedoch die Elemente in der Mitte jeder Ebene nicht gedreht. Natürlich brauchen wir eine Möglichkeit, innerhalb einer Ebene zu iterieren.
Das Problem ist, dass die einzige Schleife in unserer Funktion (unsere Ebenenschleife) bei jeder Iteration zur nächsten Ebene wechselt. Da unsere Matrix nur eine drehbare Ebene hat, wird die Ebenenschleife beendet, nachdem nur die Ecken gedreht wurden. Schauen wir uns an, was mit einer größeren 5 × 5-Matrix passiert (bei der zwei Schichten gedreht werden müssen). Der Funktionscode wurde weggelassen, bleibt aber derselbe wie oben:
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)
Die Ausgabe ist:
[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]
Es sollte keine Überraschung sein, dass die Ecken der äußersten Ebene gedreht wurden, aber Sie können auch feststellen, dass die Ecken der nächsten Ebene (nach innen) ebenfalls gedreht wurden. Das macht Sinn. Wir haben Code geschrieben, um durch Ebenen zu navigieren und die Ecken jeder Ebene zu drehen. Das fühlt sich nach Fortschritt an, aber leider müssen wir einen Schritt zurücktreten. Es ist einfach nicht gut, zur nächsten Ebene zu wechseln, bis die vorherige (äußere) Ebene vollständig gedreht wurde. Das heißt, bis jedes Element in der Ebene gedreht wurde. Nur die Ecken zu drehen reicht nicht!
Tief durchatmen. Wir brauchen eine andere Schleife. Eine verschachtelte Schleife nicht weniger. Die neue, verschachtelte Schleife wird die Verwendung first
und last
Variablen, plus ein Offset innerhalb einer Schicht zu navigieren. Wir werden diese neue Schleife unsere "Elementschleife" nennen. Die Elementschleife besucht jedes Element in der oberen Reihe, jedes Element auf der rechten Seite, jedes Element in der unteren Reihe und jedes Element auf der linken Seite.
- Wenn Sie in der oberen Zeile vorwärts gehen, muss der Spaltenindex erhöht werden.
- Wenn Sie sich auf der rechten Seite nach unten bewegen, muss der Zeilenindex erhöht werden.
- Wenn Sie sich am unteren Rand rückwärts bewegen, muss der Spaltenindex dekrementiert werden.
- Wenn Sie die linke Seite nach oben bewegen, muss der Zeilenindex dekrementiert werden.
Das klingt komplex, ist aber einfach, da die Häufigkeit, mit der wir inkrementieren und dekrementieren, um das oben Gesagte zu erreichen, auf allen vier Seiten der Matrix gleich bleibt. Zum Beispiel:
- Bewegen Sie 1 Element über die oberste Reihe.
- Bewegen Sie 1 Element auf der rechten Seite nach unten.
- Bewegen Sie 1 Element entlang der unteren Reihe nach hinten.
- Bewegen Sie 1 Element auf der linken Seite nach oben.
Dies bedeutet, dass wir eine einzelne Variable in Kombination mit den Variablen first
und verwenden last
können, um sich innerhalb einer Ebene zu bewegen. Es kann hilfreich sein zu beachten, dass für das Bewegen über die obere Reihe und nach unten auf der rechten Seite ein Inkrementieren erforderlich ist. Während Sie sich entlang der unteren und oberen linken Seite rückwärts bewegen, müssen beide dekrementiert werden.
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)
Jetzt müssen wir nur noch die Oberseite der rechten Seite, die rechte Seite der Unterseite, die Unterseite der linken Seite und die linke Seite der Oberseite zuweisen. Wenn wir das alles zusammenfügen, erhalten wir:
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
Angesichts der Matrix:
0, 1, 2
3, 4, 5
6, 7, 8
Unsere rotate
Funktion ergibt:
6, 3, 0
7, 4, 1
8, 5, 2