Inspiriert von Raymond Chens Beitrag , sagen Sie, Sie haben ein zweidimensionales 4x4-Array, schreiben Sie eine Funktion, die es um 90 Grad dreht. Raymond verlinkt auf eine Lösung im Pseudocode, aber ich würde gerne einige Dinge aus der realen Welt sehen.
[1][2][3][4]
[5][6][7][8]
[9][0][1][2]
[3][4][5][6]
Wird:
[3][9][5][1]
[4][0][6][2]
[5][1][7][3]
[6][2][8][4]
Update : Nicks Antwort ist die einfachste, aber gibt es eine Möglichkeit, es besser zu machen als n ^ 2? Was wäre, wenn die Matrix 10000x10000 wäre?
algorithm
matrix
multidimensional-array
swilliams
quelle
quelle
Antworten:
Hier ist es in 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; }
quelle
ret[i][j] = matrix[j][n - i - 1]
O (n ^ 2) Zeit- und O (1) Raumalgorithmus (ohne Problemumgehungen und Taschentuch!)
Um +90 drehen:
Um -90 drehen:
Methode 1 :
Methode 2:
Um +180 drehen:
Methode 1 : Zweimal um +90 drehen
Methode 2 : Jede Zeile umkehren und dann jede Spalte umkehren (Transponieren)
Um -180 drehen:
Methode 1 : Zweimal um -90 drehen
Methode 2 : Kehren Sie jede Spalte um und kehren Sie dann jede Zeile um
Methode 3 : Drehen Sie um +180, da sie gleich sind
quelle
rotateCW = map reverse . transpose
undrotateCCW = transpose . map reverse
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:
Wenn wir dies um 90 Grad drehen, erhalten wir:
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:
Die Matrix wird mithilfe eines zweidimensionalen Arrays definiert:
Daher greift die erste Indexposition auf die Zeile zu. Die zweite Indexposition greift auf die Spalte zu:
Wir definieren eine Utility-Funktion zum Drucken einer Matrix.
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
Eine 4 × 4-Matrix hat 2 Schichten
Eine 5 × 5-Matrix hat 3 Schichten
Eine 6 × 6-Matrix hat 3 Schichten
Eine 7 × 7-Matrix hat 4 Schichten
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:
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:
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.
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:
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:
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:
0 und 4 sind auch die Positionen der Zeilen für die äußerste Schicht.
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.
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".
Der obige Code durchläuft die Positionen (Zeilen und Spalten) aller Ebenen, die gedreht werden müssen.
Wir haben jetzt eine Schleife, die die Positionen der Zeilen und Spalten jeder Ebene angibt. Die Variablen
first
undlast
identifizieren die Indexposition der ersten und letzten Zeilen und Spalten. Zurück zu unseren Zeilen- und Spaltentabellen: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.
Unsere Ebenenschleife enthält die Indizes der ersten und letzten Spalte sowie der ersten und letzten Zeile:
Da unsere Matrizen immer quadratisch sind, benötigen wir nur zwei Variablen,
first
undlast
da die Indexpositionen für Zeilen und Spalten gleich sind.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
undlast
(ohne Subtraktion, Addition oder Offset dieser Variablen) definiert werden können:Aus diesem Grund beginnen wir unsere Drehung an den äußeren vier Ecken - wir drehen diese zuerst. Lassen Sie uns sie mit hervorheben
*
.Wir wollen jedes
*
mit*
dem rechts davon tauschen . Drucken wir also unsere Ecken aus, die nur mit verschiedenen Permutationen vonfirst
und definiert wurdenlast
:Die Ausgabe sollte sein:
Jetzt können wir ganz einfach jede der Ecken innerhalb unserer Ebenenschleife vertauschen:
Matrix vor dem Drehen von Ecken:
Matrix nach rotierenden Ecken:
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:
Die Ausgabe ist:
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
undlast
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.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:
Dies bedeutet, dass wir eine einzelne Variable in Kombination mit den Variablen
first
und verwendenlast
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.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:
Angesichts der Matrix:
Unsere
rotate
Funktion ergibt:quelle
list(zip(*reversed(your_list_of_lists)))
Python:
rotated = list(zip(*original[::-1]))
und gegen den Uhrzeigersinn:
rotated_ccw = list(zip(*original))[::-1]
So funktioniert das:
zip(*original)
tauscht Achsen von 2D-Arrays aus, indem entsprechende Elemente aus Listen in neue Listen gestapelt werden. (Der*
Operator weist die Funktion an, die enthaltenen Listen in Argumente zu verteilen.)Die
[::-1]
Anweisung kehrt Array-Elemente um (siehe Extended Slices oder diese Frage ):Schließlich führt die Kombination der beiden zur Rotationstransformation.
Durch die Änderung der Platzierung von
[::-1]
werden Listen in verschiedenen Ebenen der Matrix umgekehrt.quelle
zip(*reversed(original))
stattdessen verwendenzip(*original[::-1])
, um zu vermeiden, dass eine zusätzliche Kopie der ursprünglichen Liste erstellt wird.Hier ist eine, die die Drehung an Ort und Stelle ausführt, anstatt ein völlig neues Array zu verwenden, um das Ergebnis zu speichern. Ich habe die Initialisierung des Arrays abgebrochen und ausgedruckt. Dies funktioniert nur für quadratische Arrays, sie können jedoch beliebig groß sein. Der Speicheraufwand entspricht der Größe eines Elements des Arrays, sodass Sie ein beliebig großes Array drehen können.
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; } }
quelle
Es gibt hier jede Menge guten Codes, aber ich möchte nur zeigen, was geometrisch vor sich geht, damit Sie die Codelogik ein wenig besser verstehen können. Hier ist, wie ich das angehen würde.
Verwechseln Sie dies zunächst nicht mit einer sehr einfachen Umsetzung.
Die Grundidee ist, es als Schichten zu behandeln und wir drehen jeweils eine Schicht nach der anderen.
Sagen wir, wir haben einen 4x4
Nachdem wir es um 90 im Uhrzeigersinn gedreht haben, erhalten wir
Zerlegen wir dies also. Zuerst drehen wir die 4 Ecken im Wesentlichen
dann drehen wir den folgenden Diamanten, der irgendwie schief ist
und dann der 2. schiefe Diamant
Das kümmert sich also um die Außenkante, so dass wir im Wesentlichen diese eine Schale nach der anderen machen, bis
schließlich das mittlere Quadrat (oder wenn es seltsam ist, nur das letzte Element, das sich nicht bewegt)
Lassen Sie uns nun die Indizes jeder Ebene herausfinden. Nehmen wir an, wir arbeiten immer mit der äußersten Ebene, die wir gerade ausführen
so weiter und so fort, bis wir auf halbem Weg sind
so ist im Allgemeinen das Muster
quelle
Wie ich in meinem vorherigen Beitrag sagte, ist hier ein Code in C #, der eine O (1) -Matrixrotation für eine Matrix beliebiger Größe implementiert. Der Kürze und Lesbarkeit halber gibt es keine Fehler- oder Bereichsprüfung. Der Code:
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, ich werde meine Hand heben, es werden beim Drehen keine Änderungen am ursprünglichen Array vorgenommen. In einem OO-System spielt dies jedoch keine Rolle, solange das Objekt so aussieht, als wäre es für die Clients der Klasse gedreht worden. Im Moment verwendet die Matrix-Klasse Verweise auf die ursprünglichen Array-Daten, sodass durch Ändern eines beliebigen Werts von m1 auch m2 und m3 geändert werden. Eine kleine Änderung am Konstruktor, um ein neues Array zu erstellen und die Werte dorthin zu kopieren, wird das klären.
quelle
m_matrix.GetUpperBound()
Anrufen und der Vermittlung optimiert werden . DanachMatrix[width-x,height-y]
läuft es darauf hinaus, auf das Basis-Array zuzugreifen, das nicht so weit entferntMatrix[x,y]
ist. In meinen Tests war es die gleiche Geschwindigkeit. Ein weiterer Vorteil ist, dass Sie an noch größeren Berechnungen oder Bildern arbeiten können, da Sie diese nicht zweimal speichern müssen. Es ist vollständig parallelisierbar, kein Austausch von Zeilen / Elementen, kein Kopieren des Speichers. Gleichmäßiges Caching ist 2 For-Schleifen mit optimalem O (N * N).Während das Drehen der Daten an Ort und Stelle erforderlich sein kann (möglicherweise um die physisch gespeicherte Darstellung zu aktualisieren), wird es einfacher und möglicherweise leistungsfähiger, dem Arrayzugriff eine Indirektionsebene hinzuzufügen, möglicherweise eine Schnittstelle:
interface IReadableMatrix { int GetValue(int x, int y); }
Wenn Sie
Matrix
diese Schnittstelle bereits implementiert haben, kann sie über eine Dekorationsklasse wie die folgende gedreht werden :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); } }
Auf diese Weise können auch + 90 / -90 / 180 Grad gedreht, horizontal / vertikal gespiegelt und skaliert werden.
Die Leistung müsste in Ihrem spezifischen Szenario gemessen werden. Die O (n ^ 2) -Operation wurde jetzt jedoch durch einen O (1) -Aufruf ersetzt. Es ist eine virtuelle Call - Methode , die ist langsamer als die direkte Array - Zugriff, so hängt es davon ab, wie häufig die gedrehte Anordnung nach der Drehung verwendet wird. Wenn es einmal verwendet wird, würde dieser Ansatz definitiv gewinnen. Wenn es gedreht und dann tagelang in einem System mit langer Laufzeit verwendet wird, ist die Rotation an Ort und Stelle möglicherweise besser. Es hängt auch davon ab, ob Sie die Vorabkosten akzeptieren können.
Wie bei allen Leistungsproblemen messen, messen, messen!
quelle
Dies ist eine bessere Version davon in Java: Ich habe es für eine Matrix mit einer anderen Breite und Höhe gemacht
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; }
Dieser Code basiert auf dem Beitrag von Nick Berardi.
quelle
Rubinweg:
.transpose.map &:reverse
quelle
array.reverse.transpose
Dreht ein Array im Uhrzeigersinn, währendarray.transpose.reverse
es gegen den Uhrzeigersinn gedreht wird. Es besteht keine Notwendigkeit fürmap
.Es gibt bereits viele Antworten, und ich habe zwei gefunden, die O (1) -Zeitkomplexität beanspruchen. Der eigentliche O (1) -Algorithmus besteht darin, den Array-Speicher unberührt zu lassen und die Indizierung seiner Elemente zu ändern. Das Ziel hierbei ist, dass es weder zusätzlichen Speicher verbraucht noch zusätzliche Zeit benötigt, um die Daten zu iterieren.
Drehungen von 90, -90 und 180 Grad sind einfache Transformationen, die ausgeführt werden können, solange Sie wissen, wie viele Zeilen und Spalten sich in Ihrem 2D-Array befinden. Um einen Vektor um 90 Grad zu drehen, tauschen Sie die Achsen und negieren Sie die Y-Achse. Vertauschen Sie für -90 Grad die Achsen und negieren Sie die X-Achse. Negieren Sie für 180 Grad beide Achsen ohne zu tauschen.
Weitere Transformationen sind möglich, z. B. horizontales und / oder vertikales Spiegeln, indem die Achsen unabhängig voneinander negiert werden.
Dies kann beispielsweise durch eine Zugriffsmethode erfolgen. Die folgenden Beispiele sind JavaScript-Funktionen, die Konzepte gelten jedoch für alle Sprachen gleichermaßen.
// 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);
Dieser Code setzt ein Array verschachtelter Arrays voraus, wobei jedes innere Array eine Zeile ist.
Mit dieser Methode können Sie Elemente (auch in zufälliger Reihenfolge) lesen (oder schreiben), als ob das Array gedreht oder transformiert worden wäre. Wählen Sie jetzt einfach die richtige Funktion zum Aufrufen aus, wahrscheinlich als Referenz, und los geht's!
Das Konzept kann erweitert werden, um Transformationen additiv (und zerstörungsfrei) über die Zugriffsmethoden anzuwenden. Einschließlich beliebiger Winkelrotationen und Skalierung.
quelle
Einige Leute haben bereits Beispiele angeführt, bei denen ein neues Array erstellt wird.
Ein paar andere Dinge zu beachten:
(a) Anstatt die Daten tatsächlich zu verschieben, durchlaufen Sie das "gedrehte" Array einfach anders.
(b) Das Drehen an Ort und Stelle kann etwas schwieriger sein. Sie benötigen ein wenig Kratzer (wahrscheinlich ungefähr gleich einer Zeile oder Spalte). Es gibt ein altes ACM-Papier über das Durchführen von Transponierungen an Ort und Stelle ( http://doi.acm.org/10.1145/355719.355729 ), aber ihr Beispielcode ist böses, mit GOTO beladenes FORTRAN.
Nachtrag:
http://doi.acm.org/10.1145/355611.355612 ist ein weiterer, angeblich überlegener Transponierungsalgorithmus an Ort und Stelle.
quelle
Nicks Antwort würde auch für ein NxM-Array mit nur einer kleinen Modifikation funktionieren (im Gegensatz zu einem NxN).
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];
Eine Möglichkeit, darüber nachzudenken, besteht darin, dass Sie den Mittelpunkt der Achse (0,0) von der oberen linken Ecke zur oberen rechten Ecke verschoben haben. Sie transponieren einfach von einem zum anderen.
quelle
Zeit - O (N), Raum - O (1)
quelle
Hier ist meine Ruby-Version (beachten Sie, dass die Werte nicht gleich angezeigt werden, sie sich jedoch wie beschrieben drehen).
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))
Die Ausgabe:
quelle
Hier ist eine In-Space-Rotationsmethode von Java, nur für Quadrat. Für nicht quadratische 2D-Arrays müssen Sie ohnehin ein neues Array erstellen.
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; } } } }
Code zum Drehen eines 2D-Arrays beliebiger Größe durch Erstellen eines neuen Arrays:
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; }
quelle
Implementierung des +90-Pseudocodes von dimple (z. B. transponieren und dann jede Zeile umkehren) in JavaScript:
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; }
quelle
Sie können dies in 3 einfachen Schritten tun :
1 ) Angenommen, wir haben eine Matrix
2 ) Nehmen Sie die Transponierte der Matrix
3 ) Vertauschen Sie die Zeilen, um eine gedrehte Matrix zu erhalten
Java- Quellcode dafür:
Ausgabe:
quelle
Dies ist meine Implementierung in C, O (1) -Speicherkomplexität an Ort und Stelle um 90 Grad im Uhrzeigersinn:
#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; } } }
quelle
Hier ist die Java-Version:
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; } } }
Bei dieser Methode wird zuerst die äußerste Schicht gedreht und dann die innere Schicht nach rechts verschoben.
quelle
Betrachten Sie aus linearer Sicht die Matrizen:
Nehmen Sie jetzt eine Transponierte
Und betrachten Sie die Aktion von A 'auf B oder B auf A'.
Beziehungsweise:
Dies ist für jede nxn-Matrix erweiterbar. Und dieses Konzept schnell im Code anwenden:
quelle
C # -Code zum Drehen von [n, m] 2D-Arrays um 90 Grad nach rechts
Ergebnis:
quelle
PHP:
Ab PHP5.6 kann die Array-Transposition mit einem Sleak durchgeführt werden
array_map()
Aufruf durchgeführt werden. Mit anderen Worten, Spalten werden in Zeilen konvertiert.Code: ( Demo )
$ transponiert:
quelle
For i:= 0 to X do For j := 0 to X do graphic[j][i] := graphic2[X-i][j]
X ist die Größe des Arrays, in dem sich die Grafik befindet.
quelle
#transpose ist eine Standardmethode der Ruby-Array-Klasse.
Die Implementierung ist eine in C geschriebene n ^ 2-Transpositionsfunktion. Sie können sie hier sehen: http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-transpose durch Auswahl von "click" Quelle "neben" transponieren "umschalten.
Ich erinnere mich besser als O (n ^ 2) -Lösungen, aber nur für speziell konstruierte Matrizen (wie spärliche Matrizen)
quelle
C-Code für die Matrixdrehung um 90 Grad im Uhrzeigersinn IN PLACE für jede M * N-Matrix
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; } } }
quelle
Hier ist meine In-Place-Implementierung in 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; } } }
quelle
Hier ist mein Versuch einer Matrix-90-Grad-Drehung, die eine 2-Stufen-Lösung in C ist. Transponieren Sie zuerst die Matrix an Ort und Stelle und tauschen Sie dann die Spalten aus.
#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; }
quelle
@ Tagorym: Aw, Mann. Ich hatte daran als gutes "Ich bin gelangweilt, was kann ich denken" -Puzzlespiel festgehalten. Ich habe mir meinen Transpositionscode ausgedacht, bin aber hierher gekommen, um Ihren Code zu finden, der ziemlich identisch mit meinem ist ... ah, na ja. Hier ist es in 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
quelle
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]; } }
Einfache C ++ - Methode, obwohl in einem großen Array ein großer Speicheraufwand anfällt.
quelle