Inspirado por la publicación de Raymond Chen , digamos que tiene una matriz bidimensional 4x4, escriba una función que la rote 90 grados. Raymond se vincula a una solución en pseudocódigo, pero me gustaría ver algunas cosas del mundo real.
[1][2][3][4]
[5][6][7][8]
[9][0][1][2]
[3][4][5][6]
Se convierte en:
[3][9][5][1]
[4][0][6][2]
[5][1][7][3]
[6][2][8][4]
Actualización : la respuesta de Nick es la más sencilla, pero ¿hay alguna manera de hacerlo mejor que n ^ 2? ¿Qué pasa si la matriz era 10000x10000?
algorithm
matrix
multidimensional-array
swilliams
fuente
fuente
Respuestas:
Aquí está en C #
fuente
ret[i][j] = matrix[j][n - i - 1]
O (n ^ 2) tiempo y O (1) algoritmo de espacio (¡sin ninguna solución y cosas de hanky-panky!)
Rotar por +90:
Rotar por -90:
Método 1 :
Método 2:
Rotar por +180:
Método 1 : gire +90 dos veces
Método 2 : Invierta cada fila y luego invierta cada columna (Transposición)
Rotar por -180:
Método 1 : rotar -90 dos veces
Método 2 : invierta cada columna y luego invierta cada fila
Método 3 : Rotar +180 ya que son lo mismo
fuente
rotateCW = map reverse . transpose
yrotateCCW = transpose . map reverse
Me gustaría agregar un poco más de detalle. En esta respuesta, los conceptos clave se repiten, el ritmo es lento e intencionalmente repetitivo. La solución proporcionada aquí no es la más sintácticamente compacta, sin embargo, está destinada a aquellos que desean aprender qué es la rotación de matrices y la implementación resultante.
En primer lugar, ¿qué es una matriz? Para los fines de esta respuesta, una matriz es solo una cuadrícula donde el ancho y la altura son iguales. Tenga en cuenta que el ancho y el alto de una matriz pueden ser diferentes, pero para simplificar, este tutorial considera solo matrices con igual ancho y alto ( matrices cuadradas ). Y sí, matrices es el plural de matrix.
Los ejemplos de matrices son: 2 × 2, 3 × 3 o 5 × 5. O, más generalmente, N × N. Una matriz 2 × 2 tendrá 4 cuadrados porque 2 × 2 = 4. Una matriz de 5 × 5 tendrá 25 cuadrados porque 5 × 5 = 25. Cada cuadrado se llama elemento o entrada. Representaremos cada elemento con un punto (
.
) en los siguientes diagramas:Matriz 2 × 2
Matriz 3 × 3
Matriz 4 × 4
Entonces, ¿qué significa rotar una matriz? Tomemos una matriz de 2 × 2 y pongamos algunos números en cada elemento para que se pueda observar la rotación:
Girar esto 90 grados nos da:
Literalmente giramos toda la matriz una vez hacia la derecha, como si girara el volante de un automóvil. Puede ser útil pensar en "inclinar" la matriz sobre su lado derecho. Queremos escribir una función, en Python, que tome una matriz y gire una vez hacia la derecha. La firma de la función será:
La matriz se definirá utilizando una matriz bidimensional:
Por lo tanto, la primera posición de índice accede a la fila. La segunda posición del índice accede a la columna:
Definiremos una función de utilidad para imprimir una matriz.
Un método para rotar una matriz es hacerlo capa por capa. ¿Pero qué es una capa? Piensa en una cebolla. Al igual que las capas de una cebolla, a medida que se elimina cada capa, nos movemos hacia el centro. Otras analogías es una muñeca Matryoshka o un juego de pasar el paquete.
El ancho y la altura de una matriz dictan el número de capas en esa matriz. Usemos diferentes símbolos para cada capa:
Una matriz 2 × 2 tiene 1 capa
Una matriz 3 × 3 tiene 2 capas.
Una matriz 4 × 4 tiene 2 capas.
Una matriz de 5 × 5 tiene 3 capas.
Una matriz de 6 × 6 tiene 3 capas.
Una matriz de 7 × 7 tiene 4 capas.
Puede notar que al incrementar el ancho y la altura de una matriz en uno, no siempre aumenta el número de capas. Tomando las matrices anteriores y tabulando las capas y dimensiones, vemos que el número de capas aumenta una vez por cada dos incrementos de ancho y alto:
Sin embargo, no todas las capas necesitan rotación. Una matriz 1 × 1 es la misma antes y después de la rotación. La capa central 1 × 1 siempre es la misma antes y después de la rotación, sin importar qué tan grande sea la matriz general:
Dada la matriz N × N, ¿cómo podemos determinar mediante programación el número de capas que necesitamos rotar? Si dividimos el ancho o la altura entre dos e ignoramos el resto, obtenemos los siguientes resultados.
¿Observa cómo
N/2
coincide el número de capas que deben rotarse? A veces, el número de capas giratorias es uno menos el número total de capas en la matriz. Esto ocurre cuando la capa más interna está formada por un solo elemento (es decir, una matriz 1 × 1) y, por lo tanto, no necesita ser rotada. Simplemente se ignora.Sin duda, necesitaremos esta información en nuestra función para rotar una matriz, así que añádala ahora:
Ahora que sabemos qué capas son y cómo determinar el número de capas que realmente necesitan rotación, ¿cómo aislamos una sola capa para poder rotarla? En primer lugar, inspeccionamos una matriz desde la capa más externa, hacia adentro, hasta la capa más interna. Una matriz de 5 × 5 tiene tres capas en total y dos capas que necesitan rotación:
Veamos primero las columnas. La posición de las columnas que definen la capa más externa, suponiendo que contamos desde 0, son 0 y 4:
0 y 4 también son las posiciones de las filas para la capa más externa.
Este siempre será el caso, ya que el ancho y la altura son los mismos. Por lo tanto, podemos definir las posiciones de columna y fila de una capa con solo dos valores (en lugar de cuatro).
Moviéndose hacia adentro a la segunda capa, la posición de las columnas es 1 y 3. Y, sí, lo adivinó, es lo mismo para las filas. Es importante comprender que tuvimos que aumentar y disminuir las posiciones de las filas y columnas al movernos hacia la siguiente capa.
Entonces, para inspeccionar cada capa, queremos un bucle con contadores crecientes y decrecientes que representen moverse hacia adentro, comenzando desde la capa más externa. Llamaremos a esto nuestro 'bucle de capa'.
El código anterior recorre las posiciones (fila y columna) de cualquier capa que necesite rotación.
Ahora tenemos un bucle que proporciona las posiciones de las filas y columnas de cada capa. Las variables
first
elast
identifican la posición del índice de las primeras y últimas filas y columnas. Volviendo a nuestras tablas de filas y columnas:Entonces podemos navegar a través de las capas de una matriz. Ahora necesitamos una forma de navegar dentro de una capa para poder mover elementos alrededor de esa capa. Tenga en cuenta que los elementos nunca 'saltan' de una capa a otra, pero se mueven dentro de sus respectivas capas.
Al girar cada elemento en una capa, se rota toda la capa. Al girar todas las capas en una matriz, se rota toda la matriz. Esta oración es muy importante, así que por favor, intente comprenderla antes de continuar.
Ahora, necesitamos una forma de mover elementos, es decir, rotar cada elemento y, posteriormente, la capa y, finalmente, la matriz. Para simplificar, volveremos a una matriz de 3x3, que tiene una capa giratoria.
Nuestro bucle de capa proporciona los índices de la primera y la última columna, así como la primera y la última fila:
Debido a que nuestras matrices son siempre cuadradas, solo necesitamos dos variables
first
ylast
, dado que las posiciones de índice son las mismas para filas y columnas.Las variables primero y último se pueden usar fácilmente para hacer referencia a las cuatro esquinas de una matriz. Esto se debe a que las esquinas en sí pueden definirse usando varias permutaciones de
first
ylast
(sin sustracción, suma ni compensación de esas variables):Por esta razón, comenzamos nuestra rotación en las cuatro esquinas exteriores; las rotaremos primero. Destaquémoslos con
*
.Queremos intercambiar cada una
*
con el*
de la derecha de la misma. Así que sigamos imprimiendo nuestras esquinas definidas usando solo varias permutaciones defirst
ylast
:La salida debe ser:
Ahora podríamos intercambiar fácilmente cada una de las esquinas desde nuestro bucle de capa:
Matriz antes de girar esquinas:
Matriz después de rotar esquinas:
¡Excelente! Hemos rotado con éxito cada esquina de la matriz. Pero no hemos rotado los elementos en el medio de cada capa. Claramente, necesitamos una forma de iterar dentro de una capa.
El problema es que el único bucle en nuestra función hasta ahora (nuestro bucle de capa) se mueve a la siguiente capa en cada iteración. Como nuestra matriz tiene solo una capa giratoria, el bucle de capa sale después de rotar solo las esquinas. Veamos qué sucede con una matriz más grande de 5 × 5 (donde dos capas necesitan rotación). El código de función se ha omitido, pero sigue siendo el mismo que el anterior:
El resultado es:
No debería sorprender que las esquinas de la capa más externa se hayan girado, pero también puede notar que las esquinas de la siguiente capa (hacia adentro) también se han girado. Esto tiene sentido. Hemos escrito código para navegar por las capas y también para rotar las esquinas de cada capa. Esto se siente como un progreso, pero desafortunadamente debemos dar un paso atrás. Simplemente no es bueno pasar a la siguiente capa hasta que la capa anterior (exterior) se haya rotado por completo. Es decir, hasta que cada elemento en la capa haya sido girado. ¡Girar solo las esquinas no funcionará!
Tomar una respiración profunda. Necesitamos otro bucle. Un bucle anidado no menos. El nuevo bucle anidado utilizará las variables
first
ylast
, más un desplazamiento para navegar dentro de una capa. Llamaremos a este nuevo bucle nuestro 'bucle de elementos'. El bucle de elementos visitará cada elemento a lo largo de la fila superior, cada elemento en el lado derecho, cada elemento en la fila inferior y cada elemento en el lado izquierdo.Esto suena complejo, pero se hace fácil porque el número de veces que incrementamos y disminuimos para lograr lo anterior sigue siendo el mismo en los cuatro lados de la matriz. Por ejemplo:
Esto significa que podemos usar una sola variable en combinación con las variables
first
ylast
para movernos dentro de una capa. Puede ser útil notar que moverse por la fila superior y hacia abajo por el lado derecho requiere un incremento. Mientras se mueve hacia atrás a lo largo de la parte inferior y hacia arriba del lado izquierdo, ambos requieren disminución.Ahora simplemente tenemos que asignar la parte superior al lado derecho, el lado derecho al fondo, el fondo al lado izquierdo y el lado izquierdo a la parte superior. Al poner todo esto juntos obtenemos:
Dada la matriz:
Nuestra
rotate
función da como resultado:fuente
list(zip(*reversed(your_list_of_lists)))
Pitón:
y en sentido antihorario:
Cómo funciona esto:
zip(*original)
intercambiará ejes de matrices 2d al apilar los elementos correspondientes de las listas en nuevas listas. (El*
operador le dice a la función que distribuya las listas contenidas en argumentos)La
[::-1]
declaración invierte los elementos de la matriz (consulte Slices extendidos o esta pregunta ):Finalmente, combinar los dos dará como resultado la transformación de rotación.
El cambio en la ubicación de
[::-1]
invertirá listas en diferentes niveles de la matriz.fuente
zip(*reversed(original))
lugar dezip(*original[::-1])
evitar crear una copia adicional de la lista original.Aquí hay uno que hace la rotación en su lugar en lugar de usar una matriz completamente nueva para mantener el resultado. Dejé la inicialización de la matriz y la imprimí. Esto solo funciona para matrices cuadradas, pero pueden ser de cualquier tamaño. La sobrecarga de memoria es igual al tamaño de un elemento de la matriz para que pueda hacer la rotación de una matriz tan grande como desee.
fuente
Aquí hay toneladas de código bueno, pero solo quiero mostrar lo que está sucediendo geométricamente para que pueda comprender un poco mejor la lógica del código. Así es como abordaría esto.
En primer lugar, no confunda esto con la transposición, que es muy fácil.
La idea básica es tratarlo como capas y rotar una capa a la vez.
decimos que tenemos un 4x4
después de rotarlo en el sentido de las agujas del reloj 90, obtenemos
así que descompongamos esto, primero rotamos las 4 esquinas esencialmente
luego rotamos el siguiente diamante que está torcido
y luego el segundo diamante sesgado
de modo que se cuide el borde exterior, así que esencialmente hacemos ese caparazón a la vez hasta
finalmente el cuadrado del medio (o si es extraño, solo el elemento final que no se mueve)
así que ahora descubramos los índices de cada capa, supongamos que siempre trabajamos con la capa más externa, estamos haciendo
y así sucesivamente hasta que estemos a la mitad del borde
así que en general el patrón es
fuente
Como dije en mi publicación anterior, aquí hay un código en C # que implementa una rotación de matriz O (1) para matrices de cualquier tamaño. Para mayor brevedad y legibilidad, no hay verificación de errores o verificación de rango. El código:
Bien, levantaré la mano, en realidad no hace ninguna modificación en la matriz original al rotar. Pero, en un sistema OO que no importa siempre y cuando el objeto parezca que se ha girado a los clientes de la clase. Por el momento, la clase Matrix utiliza referencias a los datos de la matriz original, por lo que cambiar cualquier valor de m1 también cambiará m2 y m3. Un pequeño cambio en el constructor para crear una nueva matriz y copiar los valores en él lo resolverá.
fuente
Si bien puede ser necesario rotar los datos en su lugar (quizás para actualizar la representación almacenada físicamente), se vuelve más simple y posiblemente más eficaz agregar una capa de indirección en el acceso a la matriz, tal vez una interfaz:
Si
Matrix
ya implementa esta interfaz, puede rotarla a través de una clase de decorador como esta:Rotar + 90 / -90 / 180 grados, voltear horizontalmente / verticalmente y escalar también se puede lograr de esta manera.
El rendimiento debería medirse en su escenario específico. Sin embargo, la operación O (n ^ 2) ahora ha sido reemplazada por una llamada O (1). Es una llamada de método virtual que es más lenta que el acceso directo a la matriz, por lo que depende de la frecuencia con la que se usa la matriz rotada después de la rotación. Si se usa una vez, entonces este enfoque definitivamente ganaría. Si se gira y luego se usa en un sistema de larga duración durante días, la rotación en el lugar podría funcionar mejor. También depende de si puede aceptar el costo inicial.
Como con todos los problemas de rendimiento, ¡mida, mida, mida!
fuente
Esta es una mejor versión en Java: lo he hecho para una matriz con un ancho y una altura diferentes
Este código se basa en la publicación de Nick Berardi.
fuente
Ruby-way:
.transpose.map &:reverse
fuente
array.reverse.transpose
gira una matriz en sentido horario, mientras que laarray.transpose.reverse
gira en sentido antihorario. No hay necesidad de esomap
.Ya hay muchas respuestas, y encontré dos que reclaman la complejidad del tiempo O (1). El algoritmo real O (1) es dejar el almacenamiento de matriz intacto y cambiar la forma en que indexa sus elementos. El objetivo aquí es que no consume memoria adicional, ni requiere tiempo adicional para iterar los datos.
Las rotaciones de 90, -90 y 180 grados son transformaciones simples que se pueden realizar siempre que sepa cuántas filas y columnas hay en su matriz 2D; Para rotar cualquier vector 90 grados, intercambie los ejes y niegue el eje Y. Para -90 grados, intercambie los ejes y niegue el eje X. Para 180 grados, niegue ambos ejes sin intercambiar.
Son posibles otras transformaciones, como la duplicación horizontal y / o vertical al negar los ejes de forma independiente.
Esto se puede hacer, por ejemplo, mediante un método de acceso. Los ejemplos a continuación son funciones de JavaScript, pero los conceptos se aplican por igual a todos los idiomas.
Este código supone una matriz de matrices anidadas, donde cada matriz interna es una fila.
El método le permite leer (o escribir) elementos (incluso en orden aleatorio) como si la matriz se hubiera girado o transformado. Ahora simplemente elija la función correcta para llamar, probablemente por referencia, ¡y listo!
El concepto se puede ampliar para aplicar transformaciones de forma aditiva (y no destructiva) a través de los métodos de acceso. Incluyendo rotaciones de ángulo arbitrarias y escalado.
fuente
Un par de personas ya han presentado ejemplos que implican hacer una nueva matriz.
Algunas otras cosas a considerar:
(a) En lugar de mover los datos, simplemente atraviese la matriz "rotada" de manera diferente.
(b) Hacer la rotación en el lugar puede ser un poco más complicado. Necesitarás un poco de lugar para rascar (probablemente aproximadamente igual a una fila o columna de tamaño). Hay un antiguo documento de ACM sobre hacer transposiciones en el lugar ( http://doi.acm.org/10.1145/355719.355729 ), pero su código de ejemplo es FORTRAN desagradable cargado de goto.
Apéndice:
http://doi.acm.org/10.1145/355611.355612 es otro algoritmo de transposición in situ supuestamente superior.
fuente
De Nick respuesta también funcionaría para una matriz NxM con solo una pequeña modificación (a diferencia de una NxN).
Una forma de pensar en esto es que ha movido el centro del eje (0,0) desde la esquina superior izquierda a la esquina superior derecha. Simplemente estás transponiendo de uno a otro.
fuente
Tiempo - O (N), Espacio - O (1)
fuente
Aquí está mi versión de Ruby (tenga en cuenta que los valores no se muestran de la misma manera, pero todavía gira como se describe).
La salida:
fuente
Aquí hay un método de rotación en el espacio, por java, solo para el cuadrado. para una matriz 2D no cuadrada, deberá crear una nueva matriz de todos modos.
código para rotar cualquier tamaño de matriz 2D creando una nueva matriz:
fuente
Implementación del pseudocódigo +90 de dimple (por ejemplo, transponer y luego invertir cada fila) en JavaScript:
fuente
Puedes hacer esto en 3 sencillos pasos :
1 ) Supongamos que tenemos una matriz
2 ) Tomar la transposición de la matriz
3 ) Intercambiar filas para obtener la matriz girada
Código fuente de Java para esto:
Salida:
fuente
Esta es mi implementación, en complejidad de memoria C, O (1), rotación en el lugar, 90 grados en sentido horario:
fuente
Aquí está la versión de Java:
El método primero gira la capa más externa, luego se mueve secuencialmente a la capa interna.
fuente
Desde un punto de vista lineal, considere las matrices:
Ahora toma una transposición
Y considere la acción de A 'sobre B, o B sobre A'.
Respectivamente:
Esto es expandible para cualquier matriz nxn. Y aplicando este concepto rápidamente en código:
fuente
Código C # para rotar [n, m] matrices 2D 90 grados a la derecha
Resultado:
fuente
PHP:
Desde PHP5.6, la transposición de matriz se puede realizar con una
array_map()
llamada lenta . En otras palabras, las columnas se convierten en filas.Código: ( Demo )
$ transpuesto:
fuente
For i:= 0 to X do For j := 0 to X do graphic[j][i] := graphic2[X-i][j]
X es el tamaño de la matriz en la que se encuentra el gráfico.
fuente
#transpose es un método estándar de la clase Ruby's Array, por lo tanto:
La implementación es una función de transposición n ^ 2 escrita en C. Puede verla aquí: http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-transpose "clic para alternar la fuente "al lado de" transponer ".
Recuerdo mejores soluciones que O (n ^ 2), pero solo para matrices especialmente construidas (como matrices dispersas)
fuente
Código C para la rotación de la matriz 90 grados en sentido horario EN EL LUGAR para cualquier matriz M * N
fuente
Aquí está mi implementación en el lugar en C
fuente
Aquí está mi intento de rotación de la matriz de 90 grados, que es una solución de 2 pasos en C. Primero transponga la matriz en su lugar y luego intercambie los cols.
fuente
@dagorym: Aw, hombre. Había estado colgándome de esto como un buen acertijo "Estoy aburrido, qué puedo reflexionar". Se me ocurrió mi código de transposición in situ, pero llegué aquí para encontrar el suyo casi idéntico al mío ... ah, bueno. Aquí está en Ruby.
fuente
Método simple de C ++, aunque habría una gran sobrecarga de memoria en una gran matriz.
fuente