Explicación y resolución: Rotar matriz n veces| Ejercicio C++


LinceAzul

Tu lince confiable
Noderador
Nodero
Noder
Pues bueno, que veo los posts de destape y me han entrado ganas de explicar algo a mi también, así que aquí vengo a aportar mi granito de arena.

No voy a explicar conceptos básicos del palo qué es una variable, una función, un array, una matriz, etc porque me da pereza y hay 2000000000 explicaciones por todos lados.
En cambio, os voy a enseñar estos conceptos aplicados a una ejercicio de examen, uno que cayó en un final de C++.


¿Cómo roto una matriz en C++?
Antes de nada, quiero aclarar mi forma de programar en C++. En vez crear y definir todas las funciones arriba y luego poner el main, para que se vea más limpito y tener el main más a mano y no bajar tropecientas líneas de código para llegar a él, primero creo las funciones arriba (con nombres descriptivos para saber qué cojones hace cada cosa) y luego las defino abajo, donde está toda la chicha.

Ahora sí, empecemos. En este caso me ha dado por poner como ejemplo una matriz 4x4, pero este proceso se puede aplicar a todo tipo de matrices. Para ello, defino una constante entera MAX con el valor 4 al principio del código (4, porque como es una matriz cuadrada y es 4x4, las filas y las columnas tienen el mismo valor y siempre que me quiera referir a cualquiera de ellas usaremos MAX).

Antes de nada, os voy a explicar cómo definir un array. La estructura para definir un array en c++ sería la siguiente: typedef array<tipo, tamaño> NombreDelArray; El typedef es para cuando quiera declarar el array, en vez de poner todo el puto rato array<tipo,tamaño> pues directamente pongo el nombre del array. Luego, el tipo, sería por ejemplo si quiero un array de enteros (int), de cadenas(string), de caracteres(char), o de lo que me salga de los cojones. Por último el tamaño, pues es el tamaño, no hay más, sería los elementos que caben en el array. si le pongo 20, pues me entran 20 elementos (desde el colocado en el índice 0, hasta el colocado en el 19, porque en c++ se empieza a contar posiciones desde el 0). Un ejemplo, sería: typedef array<int, MAX> EstoEsUnPutoArray; Por lo que, si quiero definir un array de enteros de tamaño MAX(4 en nuestro caso), pues lo haría llamándo a EstoEsUnPutoArray, por ejemplo, definamos el array vacío Paco con las condiciones de antes:
C++:
EstoEsUnArray Paco = {};

Ahora toca la definición de la matriz. Una matriz sería como un array de arrays, por lo que defino un array dentro de otro. Primero defino las filas (array TFilas, su tamaño sería el número de columnas que tendrá la matriz) y luego las columnas (el número de filas que quiero, en este caso quiero TFilas, "MAX" veces, por lo que tendré un array de 4 elementos 4 veces, lo que sería una matriz 4x4) , y ya está, así definiríamos una matriz. Yo prefiero ponerlo todo del tirón pero tienes 2 formas de definirlo, del tirón, o primero definiendo el array de filas y luego haciendo la matriz con el array de filas, se verían así las dos formas (ambas son correctas, haz lo que te salga de los cojones):

C++:
typedef array<array<int, MAX>, MAX> TMatriz1;
typedef array<int, MAX> TFilas;
typedef array<TFilas, MAX> TMatriz2;

Ahora toca la chichita, las funciones. No voy a entrar en temas de complejidad algorítmica ni mucho menos, pero así por encima que los algoritmos dependiendo de lo eficientes que sean, pues tienen un grado de complejidad que varian desde constante O(1) o logarítmica O(log n), lo que sería algo muy muy muy rápido hasta factorial O(n!) o exponencial O(2^n) o O(n^n) que son complejidades inaceptables, pocas veces (casi nunca) te harán falta algoritmos así, por lo que deberías optimizarlos. En este caso no es ultra complejo del palo complejidad factorial, pero tampoco es rapidísimo como uno lineal. Lo que tendríamos serían 3 bucles for anidados, lo que me da un poco de cáncer, pero bueno, es algo sencillito para que lo podáis entender. Ya luego hay formas de programar para crear algoritmos más eficientes como sería la programación dinámica, los algoritmos voraces, la vuelta atrás o backtracking, pero aquí no vamos a tocar ninguno de estos ya que es algo más avanzado.

Lo dicho, las funciones. Yo he hecho 3. Una para rotar la matriz una vez, otra para rotarla las veces que me salga de los cojones y otra para imprimir la matriz por pantalla.

Empezando por lo más sencillo, os explico a cómo imprimir una matriz por pantalla. Usaríamos un bucle anidado (un bucle dentro de otro), en el que imprimiríamos uno a uno los elementos de la matriz en cuestión que se accedería a ellos de la siguiente manera. Con la matriz TMatriz m, cada casilla de la matriz sería m[fila][columna] (por lo general en vez de fila y columna, por conveniencia se les llama "i" a las filas y "j" a las columnas). Accederíamos a cada fila, MAX veces que sería el tamaño de la matriz.

Defino el bucle for. Uso un bucle for porque tiene un tamaño definido, sino usaría un bucle while, que me suelen rentar cuando no sé el rango, o no sé cuándo parar. La estructura de un bucle for es sencilla. Me creo una variable interna para el bucle en la que defino un rango de veces en el que quiero que se repita lo que haya en el bucle, por ejemplo, en este caso sería desde 0 (el inicio de una fila) hasta MAX (el tamaño de un array Fila). Por último añado el contador, que significaría de cuanto en cuanto quiero recorrer el bucle. Por ejemplo si al final pongo i++ o ++i, iría de 1 en 1, si pongo i += 2, iría de 2 en 2 y así.De esta manera recorreríamos una fila con un bucle for. Pero, una matriz tiene varias filas, por eso usaríamos el doble bucle, el primero para señalar en qué fila nos encontramos y el segundo para ir de columna en columna y así imprimir cada casilla.
El esqueleto básico para imprimir una matriz es el siguiente (es siempre igual):

C++:
// Recorro filas
for(int i = 0; i < MAX; ++i){
    // Para cada fila, recorro una columna (cada casilla)
    for(int j = 0; j < MAX; ++j){
        // Imprimo los elementos de las casillas separados por espacios
        cout << m[i][j] << " ";
    }
    // Salto de linea para separar las filas, sino se vería todo en una fila
    cout << endl;
}

La función rotar_una_vez tiene como parámetro pasado por referenia (el simbolito &) la matriz que quiero rotar. Le pongo el & porque el contenido de la matriz que paso como parámetro va cambiar, por lo que o le meto el & o no hago nada. Si fuera algo como imprimir la matriz, donde no toco nada, sólo imprimo, pues no haría falta el &.

Pues bueno, me crearía una matriz auxiliar vacía aux , donde metería las soluciones de rotar la matriz una vez. Como es una matriz de enteros, al inicializarlo vacío con las dobles llaves {{}}, tendría una matriz con todo ceros :
1674441205398.png

El proceso de rotar es muy sencillo y lo voy a enseñar a lo cutre dibujado en paint :)
1674442170377.png

Para rotar la primera fila de la matriz, hacia la derecha 1 vez, pasaríamos los elementos de cada casilla como se vería en el dibujo. Con la sintaxis (i,j) siendo (fila, columna) haríamos lo siguiente:
(0,0) -> (0,3) | (1,0) -> (0,2) |
(0,1) -> (1,3) | (1,1) -> (1,2) |
(0,2) -> (2,3) | (1,2) -> (2,2) |
(0,3) -> (3,3) | (1,3) -> (3,2) |
En cada iteración, la variable i se incrementa de uno en uno, y con ese valor de i, la variable j se incrementa MAX veces, en el momento que eso se cumpla, salimos del bucle, incrementamos i una vez más y volvemos a incrementar j ,MAX veces. Ambas i y j las incremento de 1 en 1, como se vería en el ++i y ++j, que significa que le sumo 1 a la variable en cada iteración. No es necesario en los bucles siempre poner ++i, si quiero que vaya de 2 en 2 en un array, le puedo poner i += 2; por ejemplo.

Pues del esquemita de arriba, con un poquito de ojo le podemos sacar el patrón, y llegamos a la conclusión de que para rotar 1 vez una fila, su fórmula sería la siguiente:
(i,j) -> (j,MAX-1-i), siendo MAX-1 = 3, y le quitamos "i" que al principio es 0, así hasta llegar a 3 (ya que es menor estricto (<) y no menor o igual (>=), si fuera <= le restaríamos a lo sumo 4 unidades, por lo que nos saldríamos de los límites del array porque llegaríamos al caso 4-1-4 que es -1, un valor erróneo y fuera del array.

Hecho ya lo difícil, que es pensar la formulita, toca pasarlo a código, que es básicamente copiar en "aux", el resultado de la rotación de la matrix "m" y luego sobreescribir los valore de "aux" en "m":
1674442833053.png

Como es una función void pues pongo al final que m = aux; donde sobreescribiríamos en la matriz m, la solución. Si me hubiera salido de los huevos, podría haber puesto que la función en vez de ser void es de tipo Matriz y le pongo un return aux, donde devolvería directamente el resultado.

Ahora, me toca repetir este proceso las veces que me pida el usuario.
1674442953365.png

Paso como parámetro la matriz a rotar y las veces que quiero rotarla. Esas "n" veces que la quiero rotar las meto en el bucle de manera ingeniosa. ¿Por qué "n" módulo 4? pues porque al rotar una matriz 4 veces, volvería a su estado original, por lo que le añado el módulo, haciendo que sólo pueda rotarla 0, 1, 2, ó 3 veces. Por ejemplo, si la quiero rotar 1 vez, 1 % 4 = 1, si la quiero rotar 5 veces, sería lo mismo que una vez, ya que 5 % 4 = 1; igual con el resto.

Y ya estaría, dejo todo esto en el main con la matriz ya definida y palante, a rotarla las veces que quiera. También te puedes crear una función para pedir datos, pero como me da palo estar metiendo los putos datos en la consola, prefiero tener la matriz ya hechita.

Así se vería el ejercicio resuelto para rotar la matriz 5 veces:
1674443547237.png


No sé qué os ha parecido, estoy haciendo este post a las 4am me da pereza añadir, cambiar y explicar más cosas, por eso en algunos pasos me habré explicado más, mejor o peor en unos algunos puntos. Aquí os lo dejo, espero que no os parezca muy complicado, ni he revisado el post, si veis cualquier fallo ortográfico me la suda, carpe diem.
De: Vuestro lince de confianza
 

Adjuntos

  • 1674442950560.png
    1674442950560.png
    19 KB · Visitas: 3
  • 1674443269487.png
    1674443269487.png
    56 KB · Visitas: 4
Última edición:

destapeman

FUCK PUSSYS, YES BADASS
Moderador
Paladín de Nodo
Jinete de Nodo
Burgués de Nodo
Noderador
Nodero
Noder
increíble sacada de polla tío, te lo tomaste en serio JAJAJAJA muy buen post, esto para los que quieran estudiar robótica es canelita en rama.
 
  • Maravilloso
Reacciones : LinceAzul

destapeman

FUCK PUSSYS, YES BADASS
Moderador
Paladín de Nodo
Jinete de Nodo
Burgués de Nodo
Noderador
Nodero
Noder
entiendo, que de forma burda, si le añadimos la función a un objeto "rueda" dentro de una clase "coche", podríamos hacer avanzar al coche con esta función no?
 

LinceAzul

Tu lince confiable
Noderador
Nodero
Noder
entiendo, que de forma burda, si le añadimos la función a un objeto "rueda" dentro de una clase "coche", podríamos hacer avanzar al coche con esta función no?
Pues dependiendo de cómo quieras hacer el coche, yo diría que sí, así a priori, una aplicación que se me ocurre es una animación de un coche con las ruedas girando en una consola jajaj.