Inspiré par le post de Raymond Chen , disons que vous avez un tableau bidimensionnel 4x4, écrivez une fonction qui le fait pivoter de 90 degrés. Raymond établit un lien vers une solution en pseudo-code, mais j'aimerais voir des trucs du monde réel.
[1][2][3][4]
[5][6][7][8]
[9][0][1][2]
[3][4][5][6]
Devient:
[3][9][5][1]
[4][0][6][2]
[5][1][7][3]
[6][2][8][4]
Mise à jour : la réponse de Nick est la plus simple, mais existe-t-il un moyen de le faire mieux que n ^ 2? Et si la matrice était de 10000x10000?
algorithm
matrix
multidimensional-array
swilliams
la source
la source
Réponses:
Le voici en C #
la source
ret[i][j] = matrix[j][n - i - 1]
Algorithme de temps O (n ^ 2) et espace O (1) (sans solution de contournement ni truc de mordant!)
Rotation de +90:
Rotation de -90:
Méthode 1:
Méthode 2:
Rotation de +180:
Méthode 1 : rotation de +90 deux fois
Méthode 2 : inverser chaque ligne, puis inverser chaque colonne (transposer)
Rotation de -180:
Méthode 1 : rotation de -90 deux fois
Méthode 2 : inversez chaque colonne, puis inversez chaque ligne
Méthode 3 : rotation de +180 car ils sont identiques
la source
rotateCW = map reverse . transpose
etrotateCCW = transpose . map reverse
Je voudrais ajouter un peu plus de détails. Dans cette réponse, les concepts clés sont répétés, le rythme est lent et intentionnellement répétitif. La solution proposée ici n'est pas la plus compacte syntaxiquement, elle est cependant destinée à ceux qui souhaitent savoir ce qu'est la rotation matricielle et l'implémentation qui en résulte.
Tout d'abord, qu'est-ce qu'une matrice? Aux fins de cette réponse, une matrice n'est qu'une grille dont la largeur et la hauteur sont identiques. Notez que la largeur et la hauteur d'une matrice peuvent être différentes, mais pour simplifier, ce didacticiel ne considère que les matrices de largeur et de hauteur égales ( matrices carrées ). Et oui, matrices est le pluriel de matrice.
Exemples de matrices: 2 × 2, 3 × 3 ou 5 × 5. Ou, plus généralement, N × N. Une matrice 2 × 2 aura 4 carrés car 2 × 2 = 4. Une matrice 5 × 5 aura 25 carrés car 5 × 5 = 25. Chaque carré est appelé élément ou entrée. Nous représenterons chaque élément avec un point (
.
) dans les diagrammes ci-dessous:Matrice 2 × 2
Matrice 3 × 3
Matrice 4 × 4
Alors, que signifie faire pivoter une matrice? Prenons une matrice 2 × 2 et mettons quelques nombres dans chaque élément afin que la rotation puisse être observée:
Une rotation de 90 degrés nous donne:
Nous avons littéralement tourné toute la matrice une fois vers la droite, tout comme le volant d'une voiture. Il peut être utile de penser à «basculer» la matrice sur son côté droit. Nous voulons écrire une fonction, en Python, qui prend une matrice et tourne une fois vers la droite. La signature de la fonction sera:
La matrice sera définie à l'aide d'un tableau à deux dimensions:
Par conséquent, la première position d'index accède à la ligne. La deuxième position d'index accède à la colonne:
Nous allons définir une fonction utilitaire pour imprimer une matrice.
Une méthode de rotation d'une matrice consiste à le faire couche par couche. Mais qu'est-ce qu'une couche? Pensez à un oignon. Tout comme les couches d'un oignon, à mesure que chaque couche est retirée, nous nous déplaçons vers le centre. D'autres analogies sont une poupée Matryoshka ou un jeu de passe-le-colis.
La largeur et la hauteur d'une matrice déterminent le nombre de couches dans cette matrice. Utilisons différents symboles pour chaque couche:
Une matrice 2 × 2 a 1 couche
Une matrice 3 × 3 a 2 couches
Une matrice 4 × 4 a 2 couches
Une matrice 5 × 5 a 3 couches
Une matrice 6 × 6 a 3 couches
Une matrice 7 × 7 a 4 couches
Vous remarquerez peut-être que l'augmentation de la largeur et de la hauteur d'une matrice n'augmente pas toujours le nombre de couches. En prenant les matrices ci-dessus et en tabulant les couches et les dimensions, nous voyons le nombre de couches augmenter une fois tous les deux incréments de largeur et de hauteur:
Cependant, toutes les couches n'ont pas besoin de tourner. Une matrice 1 × 1 est la même avant et après rotation. La couche centrale 1 × 1 est toujours la même avant et après la rotation, quelle que soit la taille de la matrice globale:
Étant donné la matrice N × N, comment pouvons-nous déterminer par programme le nombre de couches dont nous avons besoin de tourner? Si nous divisons la largeur ou la hauteur par deux et ignorons le reste, nous obtenons les résultats suivants.
Remarquez comment
N/2
correspond le nombre de couches à faire pivoter? Parfois, le nombre de couches rotatives est inférieur de un au nombre total de couches dans la matrice. Cela se produit lorsque la couche la plus interne est formée d'un seul élément (c'est-à-dire une matrice 1 × 1) et n'a donc pas besoin d'être tournée. Il est simplement ignoré.Nous aurons sans aucun doute besoin de ces informations dans notre fonction pour faire pivoter une matrice, alors ajoutons-la maintenant:
Maintenant que nous savons ce que sont les calques et comment déterminer le nombre de calques qui doivent réellement être tournés, comment isoler un seul calque pour pouvoir le faire pivoter? Tout d'abord, nous inspectons une matrice de la couche la plus externe, vers l'intérieur, jusqu'à la couche la plus interne. Une matrice 5 × 5 a trois couches au total et deux couches qui nécessitent une rotation:
Regardons d'abord les colonnes. La position des colonnes définissant la couche la plus externe, en supposant que nous comptons à partir de 0, est 0 et 4:
0 et 4 sont également les positions des lignes pour la couche la plus externe.
Ce sera toujours le cas puisque la largeur et la hauteur sont les mêmes. Par conséquent, nous pouvons définir les positions des colonnes et des lignes d'une couche avec seulement deux valeurs (plutôt que quatre).
En se déplaçant vers la deuxième couche, la position des colonnes est 1 et 3. Et, oui, vous l'avez deviné, c'est la même chose pour les lignes. Il est important de comprendre que nous devions à la fois incrémenter et décrémenter les positions des lignes et des colonnes lors du déplacement vers la couche suivante.
Donc, pour inspecter chaque couche, nous voulons une boucle avec des compteurs à la fois croissants et décroissants qui représentent le déplacement vers l'intérieur, à partir de la couche la plus externe. Nous appellerons cela notre «boucle de couches».
Le code ci-dessus parcourt les positions (ligne et colonne) de tous les calques nécessitant une rotation.
Nous avons maintenant une boucle fournissant les positions des lignes et des colonnes de chaque couche. Les variables
first
etlast
identifient la position d'index des première et dernière lignes et colonnes. Revenons à nos tableaux de lignes et de colonnes:Nous pouvons donc naviguer à travers les couches d'une matrice. Maintenant, nous avons besoin d'un moyen de naviguer dans une couche afin de pouvoir déplacer des éléments autour de cette couche. Notez que les éléments ne «sautent» jamais d'un calque à un autre, mais ils se déplacent dans leurs calques respectifs.
La rotation de chaque élément d'un calque fait pivoter l'ensemble du calque. La rotation de tous les calques d'une matrice fait pivoter la matrice entière. Cette phrase est très importante, alors faites de votre mieux pour la comprendre avant de continuer.
Maintenant, nous avons besoin d'un moyen de déplacer réellement les éléments, c'est-à-dire de faire pivoter chaque élément, puis la couche, et finalement la matrice. Pour plus de simplicité, nous allons revenir à une matrice 3x3 - qui a une couche rotative.
Notre boucle de couche fournit les index des première et dernière colonnes, ainsi que les première et dernière lignes:
Parce que nos matrices sont toujours carrées, nous n'avons besoin que de deux variables
first
etlast
, puisque les positions d'index sont les mêmes pour les lignes et les colonnes.Les variables première et dernière peuvent facilement être utilisées pour référencer les quatre coins d'une matrice. En effet, les coins eux-mêmes peuvent être définis en utilisant différentes permutations de
first
etlast
(sans soustraction, addition ou décalage de ces variables):Pour cette raison, nous commençons notre rotation aux quatre coins extérieurs - nous allons les faire pivoter en premier. Soulignons-les avec
*
.Nous voulons échanger chacun
*
avec le*
à droite de celui-ci. Alors allons-y, imprimons nos coins définis en utilisant uniquement différentes permutations defirst
etlast
:La sortie doit être:
Maintenant, nous pouvons facilement échanger chacun des coins de notre boucle de calque:
Matrice avant de tourner les coins:
Matrice après rotation des coins:
Génial! Nous avons réussi à faire pivoter chaque coin de la matrice. Mais, nous n'avons pas fait pivoter les éléments au milieu de chaque couche. De toute évidence, nous avons besoin d'un moyen d'itérer au sein d'une couche.
Le problème est que la seule boucle de notre fonction jusqu'à présent (notre boucle de couche) passe à la couche suivante à chaque itération. Étant donné que notre matrice n'a qu'une seule couche rotative, la boucle de couche se termine après avoir tourné uniquement les coins. Voyons ce qui se passe avec une matrice 5 × 5 plus grande (où deux couches doivent tourner). Le code de fonction a été omis, mais il reste le même que ci-dessus:
La sortie est:
Il ne devrait pas être surprenant que les coins de la couche la plus externe aient été tournés, mais vous pouvez également remarquer que les coins de la couche suivante (vers l'intérieur) ont également été tournés. C'est logique. Nous avons écrit du code pour naviguer à travers les couches et également pour faire pivoter les coins de chaque couche. Cela ressemble à un progrès, mais malheureusement, nous devons prendre du recul. Il est tout simplement inutile de passer au calque suivant tant que le calque précédent (extérieur) n'a pas été complètement tourné. Autrement dit, jusqu'à ce que chaque élément du calque ait été tourné. Faire tourner uniquement les coins ne fera pas l'affaire!
Respirez profondément. Nous avons besoin d'une autre boucle. Une boucle imbriquée pas moins. La nouvelle boucle imbriquée utilisera les variables
first
etlast
, plus un décalage pour naviguer dans une couche. Nous appellerons cette nouvelle boucle notre «boucle d'élément». La boucle d'élément visitera chaque élément le long de la rangée supérieure, chaque élément en bas à droite, chaque élément le long de la rangée inférieure et chaque élément en haut à gauche.Cela semble complexe, mais c'est facile car le nombre de fois que nous incrémentons et décrémentons pour atteindre ce qui précède reste le même sur les quatre côtés de la matrice. Par exemple:
Cela signifie que nous pouvons utiliser une seule variable en combinaison avec les variables
first
etlast
pour nous déplacer dans une couche. Il peut être utile de noter que le déplacement sur la rangée supérieure et sur le côté droit nécessite une incrémentation. Tout en se déplaçant vers l'arrière le long du bas et vers le haut du côté gauche, les deux nécessitent une décrémentation.Maintenant, nous devons simplement affecter le haut au côté droit, le côté droit au bas, le bas au côté gauche et le côté gauche au haut. En rassemblant tout cela, nous obtenons:
Compte tenu de la matrice:
Notre
rotate
fonction se traduit par:la source
list(zip(*reversed(your_list_of_lists)))
Python:
et dans le sens antihoraire:
Comment cela fonctionne:
zip(*original)
permutera les axes des tableaux 2D en empilant les éléments correspondants des listes dans de nouvelles listes. (L'*
opérateur indique à la fonction de distribuer les listes contenues dans des arguments)L'
[::-1]
instruction inverse les éléments du tableau (veuillez consulter les tranches étendues ou cette question ):Enfin, la combinaison des deux entraînera la transformation de rotation.
Le changement de placement de
[::-1]
inversera les listes à différents niveaux de la matrice.la source
zip(*reversed(original))
au lieu dezip(*original[::-1])
pour éviter de créer une copie supplémentaire de la liste d'origine.En voici un qui effectue la rotation en place au lieu d'utiliser un tout nouveau tableau pour conserver le résultat. J'ai laissé l'initialisation de la matrice et l'imprimer. Cela ne fonctionne que pour les tableaux carrés, mais ils peuvent être de n'importe quelle taille. La surcharge de mémoire est égale à la taille d'un élément du tableau afin que vous puissiez faire la rotation d'un tableau aussi grand que vous le souhaitez.
la source
Il y a des tonnes de bon code ici, mais je veux juste montrer ce qui se passe géométriquement afin que vous puissiez mieux comprendre la logique du code. Voici comment j'aborderais cela.
tout d'abord, ne confondez pas cela avec une transposition qui est très facile ..
l'idée de base est de le traiter comme des couches et nous faisons tourner une couche à la fois ..
disons que nous avons un 4x4
après l'avoir tourné dans le sens horaire de 90, nous obtenons
décomposons donc cela, nous tournons d'abord les 4 coins essentiellement
puis nous tournons le diamant suivant qui est en quelque sorte de travers
puis le 2e diamant asymétrique
donc ça prend soin du bord extérieur donc essentiellement on fait ça une coquille à la fois jusqu'à
enfin le carré du milieu (ou si c'est bizarre juste le dernier élément qui ne bouge pas)
alors maintenant, calculons les indices de chaque couche, supposons que nous travaillons toujours avec la couche la plus externe, nous faisons
ainsi de suite et ainsi de suite jusqu'à ce que nous soyons à mi-chemin à travers le bord
donc en général le motif est
la source
Comme je l'ai dit dans mon post précédent, voici du code en C # qui implémente une rotation de matrice O (1) pour n'importe quelle matrice de taille. Pour plus de concision et de lisibilité, il n'y a pas de vérification d'erreur ou de vérification de plage. Le code:
OK, je vais lever la main, il ne fait aucune modification au tableau d'origine lors de la rotation. Mais, dans un système OO, cela n'a pas d'importance tant que l'objet semble avoir été tourné vers les clients de la classe. À l'heure actuelle, la classe Matrix utilise des références aux données du tableau d'origine, donc la modification de toute valeur de m1 modifiera également m2 et m3. Une petite modification apportée au constructeur pour créer un nouveau tableau et y copier les valeurs triera cela.
la source
Alors que la rotation des données en place peut être nécessaire (peut-être pour mettre à jour la représentation stockée physiquement), il devient plus simple et peut-être plus performant d'ajouter une couche d'indirection sur l'accès au tableau, peut-être une interface:
Si votre
Matrix
implémente déjà cette interface, vous pouvez la faire pivoter via une classe décoratrice comme celle-ci:Une rotation de + 90 / -90 / 180 degrés, un retournement horizontal / vertical et une mise à l'échelle peuvent également être obtenus de cette manière.
Les performances devraient être mesurées dans votre scénario spécifique. Cependant, l'opération O (n ^ 2) a maintenant été remplacée par un appel O (1). C'est un appel de méthode virtuel qui est plus lent que l'accès direct au tableau, il dépend donc de la fréquence à laquelle le tableau pivoté est utilisé après la rotation. Si elle est utilisée une fois, cette approche gagnerait définitivement. S'il est tourné puis utilisé dans un système de longue durée pendant des jours, alors la rotation en place pourrait mieux fonctionner. Cela dépend également si vous pouvez accepter le coût initial.
Comme pour tous les problèmes de performance, mesurez, mesurez, mesurez!
la source
C'est une meilleure version de celui-ci en Java: je l'ai fait pour une matrice avec une largeur et une hauteur différentes
Ce code est basé sur le post de Nick Berardi.
la source
Ruby-way:
.transpose.map &:reverse
la source
array.reverse.transpose
fait pivoter un tableau dans le sens horaire, tout en le faisantarray.transpose.reverse
tourner dans le sens antihoraire. Ce n'est pas nécessairemap
.Il y a déjà beaucoup de réponses, et j'en ai trouvé deux revendiquant une complexité temporelle O (1). Le vrai algorithme O (1) consiste à laisser le stockage du tableau intact et à changer la façon dont vous indexez ses éléments. L'objectif ici est qu'il ne consomme pas de mémoire supplémentaire et qu'il ne nécessite pas de temps supplémentaire pour itérer les données.
Les rotations de 90, -90 et 180 degrés sont de simples transformations qui peuvent être effectuées tant que vous savez combien de lignes et de colonnes se trouvent dans votre réseau 2D; Pour faire pivoter un vecteur de 90 degrés, permutez les axes et annulez l'axe Y. Pour -90 degrés, permutez les axes et annulez l'axe X. Pour 180 degrés, annulez les deux axes sans permuter.
D'autres transformations sont possibles, telles que la mise en miroir horizontale et / ou verticale en annulant les axes indépendamment.
Cela peut être fait par exemple par une méthode d'accesseur. Les exemples ci-dessous sont des fonctions JavaScript, mais les concepts s'appliquent également à toutes les langues.
Ce code suppose un tableau de tableaux imbriqués, où chaque tableau interne est une ligne.
La méthode vous permet de lire (ou d'écrire) des éléments (même dans un ordre aléatoire) comme si le tableau avait été tourné ou transformé. Maintenant, choisissez la bonne fonction à appeler, probablement par référence, et c'est parti!
Le concept peut être étendu pour appliquer des transformations de manière additive (et non destructive) via les méthodes d'accesseur. Y compris les rotations d'angle arbitraires et la mise à l'échelle.
la source
Quelques personnes ont déjà mis en place des exemples qui impliquent de créer un nouveau tableau.
Quelques autres choses à considérer:
(a) Au lieu de déplacer réellement les données, parcourez simplement le tableau "tourné" différemment.
(b) Faire la rotation sur place peut être un peu plus délicat. Vous aurez besoin d'un peu d'espace de travail (probablement à peu près égal à une ligne ou une colonne). Il existe un ancien document ACM sur les transpositions sur place ( http://doi.acm.org/10.1145/355719.355729 ), mais leur exemple de code est FORTRAN méchant et chargé.
Addenda:
http://doi.acm.org/10.1145/355611.355612 est un autre algorithme de transposition en place, soi-disant supérieur.
la source
La réponse de Nick fonctionnerait également pour un tableau NxM avec seulement une petite modification (par opposition à un NxN).
Une façon de penser à cela est que vous avez déplacé le centre de l'axe (0,0) du coin supérieur gauche vers le coin supérieur droit. Vous transposez simplement de l'un à l'autre.
la source
Temps - O (N), Espace - O (1)
la source
Voici ma version Ruby (notez que les valeurs ne sont pas affichées de la même manière, mais elles tournent toujours comme décrit).
Le résultat:
la source
voici une méthode de rotation dans l'espace, par java, uniquement pour le carré. pour un tableau 2d non carré, vous devrez de toute façon créer un nouveau tableau.
code pour faire pivoter n'importe quel tableau 2d de taille en créant un nouveau tableau:
la source
Implémentation du pseudocode +90 de fossette (par exemple transposer puis inverser chaque ligne) en JavaScript:
la source
Vous pouvez le faire en 3 étapes simples :
1 ) Supposons que nous ayons une matrice
2 ) Prendre la transposition de la matrice
3 ) Échanger des lignes pour obtenir une matrice pivotée
Code source Java pour cela:
Production:
la source
Voici mon implémentation, en complexité mémoire C, O (1), rotation en place, 90 degrés dans le sens des aiguilles d'une montre:
la source
Voici la version Java:
la méthode fait d'abord pivoter le calque le plus externe, puis se déplace vers le calque intérieur de manière carrée.
la source
D'un point de vue linéaire, considérons les matrices:
Maintenant, prenez une transposition
Et considérons l'action de A 'sur B, ou B sur A'.
Respectivement:
Ceci est extensible pour n'importe quelle matrice nxn. Et appliquer ce concept rapidement dans le code:
la source
Code C # pour faire pivoter [n, m] tableaux 2D à 90 degrés à droite
Résultat:
la source
PHP:
Depuis PHP5.6, la transposition de tableau peut être effectuée avec un sleak
array_map()
appel . En d'autres termes, les colonnes sont converties en lignes.Code: ( Démo )
$ transposé:
la source
For i:= 0 to X do For j := 0 to X do graphic[j][i] := graphic2[X-i][j]
X est la taille du tableau dans lequel se trouve le graphique.
la source
#transpose est une méthode standard de la classe Ruby's Array, donc:
L'implémentation est une fonction de transposition n ^ 2 écrite en C. Vous pouvez la voir ici: http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-transpose en choisissant "click" pour basculer la source "à côté de" transposer ".
Je me souviens mieux que les solutions O (n ^ 2), mais uniquement pour les matrices spécialement construites (telles que les matrices clairsemées)
la source
Code C pour la rotation de la matrice à 90 degrés dans le sens horaire EN PLACE pour toute matrice M * N
la source
voici mon implémentation In Place en C
la source
Voici ma tentative de rotation de la matrice à 90 degrés qui est une solution en 2 étapes en C. Transposez d'abord la matrice en place, puis échangez les cols.
la source
@dagorym: Aw, mec. J'avais accroché à cela comme un bon puzzle "Je m'ennuie, que puis-je méditer". J'ai trouvé mon code de transposition en place, mais je suis arrivé ici pour trouver le vôtre à peu près identique au mien ... ah, eh bien. Le voici en Ruby.
la source
Méthode C ++ simple, alors il y aurait une grosse surcharge de mémoire dans un grand tableau.
la source