Archive for the ‘Go’ Category

Creador de cuadrados mágicos impares, pares y de doble paridad hecho en Go

agosto 13, 2020

Un cuadrado mágico es un conjunto de números dispuestos en un cuadrado de forma tal que la suma de cada fila, columna y diagonal es el mismo número, también llamado constante mágica.

16 3 2 13
5 10 11 8
9 6 7 12
4 15 14 1

Un cuadrado mágico puede ser impar, par o de doble paridad. La diferencia entre un cuadrado mágico par y uno de doble paridad es que el cuadrado mágico par tiene un número de casillas por lado que es divisible por 2 pero no por 4 y el de doble paridad tiene un número de casillas por lado que es divisible por 4.

El programa que hice genera cuadrados mágicos impares usando el método de Loubere también conocido como método siamés. El método que utilicé para crear cuadrados mágicos pares y el que utilicé para crear cuadrados mágicos de doble paridad vienen descritos en el código fuente (ignoro como se llaman esos métodos).

Apenas estoy aprendiendo Go y este es mi segundo programa así que perdón si hago cosas que probablemente se pudieran hacer con menos código


/*     cuadrado_magico.go
 *       
 *     Copyright 2020 SALOMON RINCON TORRES 
 *      
 *     This program is free software; you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation; either version 2 of the License, or
 *     (at your option) any later version.
 *    
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *     
 *     You should have received a copy of the GNU General Public License
 *     along with this program; if not, write to the Free Software
 *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *     MA 02110-1301, USA.
 *
 * Un cuadrado mágico es un conjunto de números dispuestos en un cuadrado de forma tal
 * que la suma de cada fila, columna y diagonal es el mismo número, también llamado "constante mágica"
 * Este programa resuelve cualquier tipo de cuadrado mágico, para los impares utiliza el método Loubere
 */

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
    "strconv"
    "math"
)

func main() {
    var constanteMagica int = 0
    var numeroi int = 0
    var tipoCuadro string
    
    fmt.Println("CUADRADO MAGICO\n")
    fmt.Println("Un cuadrado mágico es un conjunto de números dispuestos en un cuadrado de forma tal")
    fmt.Println("que la suma de cada fila, columna y diagonal es el mismo número, también llamado constante mágica.\n")
    fmt.Println("Este programa resuelve cualquier tipo de cuadrado mágico, ya sea impar, par o de doble paridad")
    fmt.Println("Un cuadrado mágico par tiene un número de casillas por lado que es divisible por 2 pero no por 4")
    fmt.Println("El cuadrado mágico par más pequeño posible es 6 x 6, ya que los cuadrados mágicos de 2 x 2 no se pueden resolver")
    fmt.Println("Un cuadrado mágico de doble paridad tiene un número de casillas por lado que es divisible por 4")
    fmt.Println(strings.Repeat("*", 110))
    
    reader := bufio.NewReader(os.Stdin)
    for numeroi < 3 {
                     fmt.Print("\nCuadrado mágico de nxn, introduzca el valor de n (0 para terminar o mayor a 2 para continuar): ")
                     n, _ := reader.ReadString('\n')
    
                     // Elimina el caracter \n de la variable n
                     n = strings.TrimSuffix(n, "\n")
                     // Convierte n a tipo int64 y guarda el valor en la variable numero
                     numero, _ := strconv.ParseInt(n, 10, 64)
                     // Convierte numero de tipo int64 a numeroi de tipo int
                     numeroi = int(numero)
                     if (numeroi == 0) {
                         break;
                     }
    }
    
    if (numeroi > 0) {
        fmt.Printf("\nCuadrado mágico de %dx%d\n", numeroi, numeroi)    
        
        // Crea la matriz de nxn llamada cuadro
        cuadro := creaMatriz(numeroi)
        
        // Calcula el valor de la constante mágica
        constanteMagica = (numeroi * ((numeroi*numeroi) + 1)) / 2
	
        /* Determina si el cuadrado mágico es impar, par o de doble paridad */
        if (numeroi%2 != 0) {
            tipoCuadro = "IMPAR"
        } else if (numeroi%2 == 0 && numeroi%4 != 0) {		     
                   tipoCuadro = "PAR"
        } else if (numeroi%4 == 0) {
                   tipoCuadro = "DOBLE PARIDAD"
        }	
	
        fmt.Println("La constante mágica es",constanteMagica)
        fmt.Println("El cuadrado mágico es",tipoCuadro)
        fmt.Println(strings.Repeat("-", 50))
        
        // Genera la serie de números que se deben colocar en el cuadrado mágico (de 1 a nxn)
        serie := generaSerie(numeroi*numeroi)
	    
        switch tipoCuadro {
               case "IMPAR":
                             /* Llama a la función que resuelve cuadrados mágicos impares pasándole como parámetros
                                el cuadrado mágico vacío y la serie de números con que se debe llenar */
                             resuelveCuadradoImpar(cuadro, serie)
               case "PAR":   
                             /* Debemos dividir el cuadrado mágico en 4 secciones, cada sección será un cuadrado mágico impar
                                la esquina superior izquierda será la sección A, la esquina superior derecha será la sección C,
                                la esquina inferior izquierda será la sección D y la esquina inferior derecha serla la sección B.
                                
                                A C
                                D B
                                
                                Para saber de qué grado serán los cuadrados mágicos de cada sección, hacemos n/2, en donde n
                                es el grado de nuestro cuadrado mágico par. Si el cuadrado mágico es de grado 6, cada una de las
                                4 secciones será un cuadrado mágico de grado 3 
                             */
                             gradoSecciones := numeroi/2
                             cantNumerosxSeccion := len(serie)/4
                             
                             // Crea las cuatro secciones
                             seccionA := creaMatriz(gradoSecciones)
                             seccionB := creaMatriz(gradoSecciones)
                             seccionC := creaMatriz(gradoSecciones)
                             seccionD := creaMatriz(gradoSecciones)
		             
                             numerosSeccion := make([]int, cantNumerosxSeccion)
                             copy(numerosSeccion, serie[0:cantNumerosxSeccion])
                             /* Llama a la función que resuelve cuadrados mágicos impares pasándole como parámetros
                                el cuadrado mágico seccionA y la serie de números con que se debe llenar */
                             resuelveCuadradoImpar(seccionA, numerosSeccion)
	                     
                             copy(numerosSeccion, serie[cantNumerosxSeccion:(cantNumerosxSeccion*2)])
                             /* Llama a la función que resuelve cuadrados mágicos impares pasándole como parámetros
                                el cuadrado mágico seccionB y la serie de números con que se debe llenar */
                             resuelveCuadradoImpar(seccionB, numerosSeccion)
 	                     
                             copy(numerosSeccion, serie[cantNumerosxSeccion*2:(cantNumerosxSeccion*3)])
                             /* Llama a la función que resuelve cuadrados mágicos impares pasándole como parámetros
                                el cuadrado mágico seccionC y la serie de números con que se debe llenar */
                             resuelveCuadradoImpar(seccionC, numerosSeccion)
                             
                             copy(numerosSeccion, serie[cantNumerosxSeccion*3:(cantNumerosxSeccion*4)])
                             /* Llama a la función que resuelve cuadrados mágicos impares pasándole como parámetros
                                el cuadrado mágico seccionD y la serie de números con que se debe llenar */
                             resuelveCuadradoImpar(seccionD, numerosSeccion)
                             
                             /* Ya que se tienen resueltos los cuadrados mágicos correspondientes a cada sección,
                                determinadas casillas que caen en un patrón de la sección A se deben intercambiar con las
                                casillas que caen en ese mismo patrón en la sección D.
                                También determinadas casillas que caen en un patrón de la sección C se deben intercambiar 
                                con las casillas que caen en ese mismo patrón en la sección B.
		                
                                A continuación se muestran cuales serían los patrones en un cuadrado mágico de 10x10,
                                _________________________________________
                                |   |   |   |   |   |   |   |   |   |   |
                                | * | * | A | A | A | C | C | C | C | * |
                                |___|___|___|___|___|___|___|___|___|___|
                                |   |   |   |   |   |   |   |   |   |   |
                                | * | * | A | A | A | C | C | C | C | * |
                                |___|___|___|___|___|___|___|___|___|___|
                                |   |   |   |   |   |   |   |   |   |   |
                                | A | * | * | A | A | C | C | C | C | * |
                                |___|___|___|___|___|___|___|___|___|___|
                                |   |   |   |   |   |   |   |   |   |   |
                                | * | * | A | A | A | C | C | C | C | * |
                                |___|___|___|___|___|___|___|___|___|___|
                                |   |   |   |   |   |   |   |   |   |   |
                                | * | * | A | A | A | C | C | C | C | * |
                                |___|___|___|___|___|___|___|___|___|___|
                                |   |   |   |   |   |   |   |   |   |   |
                                | x | x | D | D | D | B | B | B | B | x |
                                |___|___|___|___|___|___|___|___|___|___|
                                |   |   |   |   |   |   |   |   |   |   |
                                | x | x | D | D | D | B | B | B | B | x |
                                |___|___|___|___|___|___|___|___|___|___|
                                |   |   |   |   |   |   |   |   |   |   |
                                | D | x | x | D | D | B | B | B | B | x |
                                |___|___|___|___|___|___|___|___|___|___|
                                |   |   |   |   |   |   |   |   |   |   |
                                | x | x | D | D | D | B | B | B | B | x |
                                |___|___|___|___|___|___|___|___|___|___|
                                |   |   |   |   |   |   |   |   |   |   |
                                | x | x | D | D | D | B | B | B | B | x |
                                |___|___|___|___|___|___|___|___|___|___|
	                
                                Los valores que se encuentran en las casillas marcadas con asterisco en la sección A se deben
                                intercambiar con los valores que se encuentran en las casillas marcadas con equis en la sección D
                                y los valores que se encuentran en las casillas marcadas con asterisco en la sección C se deben
                                intercambiar con los valores que se encuentran en las casillas marcadas con equis en la sección B.
                                
                                Para determinar cuantas columnas ocupará el patrón de la sección A utilizamos columnas = g/2, en donde
                                g es el grado correspondiente a los cuadrados mágicos que forman cada una de las secciones. En el
                                ejemplo del cuadrado mágico de orden 10, cada sección está formada por cuadrados mágicos de grado 5.
                                Por tal motivo, las columnas del patrón de la sección A en este caso son 5/2 = 2.5, Sólo se toma
                                en cuenta la parte entera (en este caso el 2), significa que tomamos 2 columnas y 2 filas, después de
                                eso, en la fila siguiente (la tercera fila) saltamos una casilla y tomamos las casillas de las 2 columnas
                                siguientes. Después se regresa a la primera columna y se toman como al inicio 2 columnas y filas.
                                
                                El patrón de la sección C está determinado por columnas = (g/2)-1. En el caso del cuadrado mágico
                                de 10x10 sería (5/2)-1 = 1.5. Igual sólo tomamos la parte entera, (es decir 1). Empezamos a contar
                                de derecha a izquierda el número de columnas que vamos a tomar, en este caso sería sólo la última columna.
                                
                                Para un cuadrado mágico de grado 6, el patrón quedaría de la siguiente forma
                                               
                                _________________________
                                |   |   |   |   |   |   |
                                | * | A | A | C | C | C |
                                |___|___|___|___|___|___|
                                |   |   |   |   |   |   |
                                | A | * | A | C | C | C |
                                |___|___|___|___|___|___|
                                |   |   |   |   |   |   |
                                | * | A | A | C | C | C |
                                |___|___|___|___|___|___|
                                |   |   |   |   |   |   |
                                | x | D | D | B | B | B |
                                |___|___|___|___|___|___|
                                |   |   |   |   |   |   |
                                | D | x | D | B | B | B |
                                |___|___|___|___|___|___|
                                |   |   |   |   |   |   |
                                | x | D | D | B | B | B |
                                |___|___|___|___|___|___|
               
                                Para la sección A columnas = 3/2 = 1.5. La parte entera es uno, así que tomamos una columna y una fila,
                                en la siguiente fila saltamos una casilla y tomamos la casilla de la columna siguiente. Regresamos a la
                                primera columna y tomamos una columna y una fila.
	                
                                Para la seccion C columnas = (3/2)-1 = 0.5. Sólo tomamos la parte entera, como es cero, significa que no
                                tomamos columna alguna de esta sección para intercambiar con la sección B.
                             */
                             
                             columnasPatron1 := gradoSecciones/2
                             columnasPatron2 := (gradoSecciones/2)-1
                             
                             /* Siguiendo el patrón descrito en los comentarios anteriores, intercambia las casillas de la sección A con
                                las casillas correspondientes de la sección D
                             */
                             temporal := 0
                             for i := 0; i < gradoSecciones; i++ {
                                 for j := 0; j < columnasPatron1; j++ {
                                     if (i == gradoSecciones/2) {
                                         temporal = seccionA[i][j+1]
                                         seccionA[i][j+1] = seccionD[i][j+1]
                                         seccionD[i][j+1] = temporal 
                                     } else {
                                             temporal = seccionA[i][j]
                                             seccionA[i][j] = seccionD[i][j]
                                             seccionD[i][j] = temporal 
                                     }
                                 }
                             }
    	                     
                             /* Siguiendo el patrón descrito en los comentarios anteriores, intercambia las casillas de la sección C con 
                                las casillas correspondientes de la sección B
                             */
                             if (columnasPatron2 > 0) {
                                 temporal = 0
                                 for i := 0; i < gradoSecciones; i++ { 
                                     for j := gradoSecciones-1; j >= gradoSecciones-columnasPatron2; j-- {
                                         temporal = seccionC[i][j]
                                         seccionC[i][j] = seccionB[i][j]
                                         seccionB[i][j] = temporal 
                                     }
                                 }
                             }
                             // Llena el cuadro mágico par copiando los valores de las 4 secciones
                             
                             // Copia los valores de la sección A
                             for i := 0; i < gradoSecciones; i++ { 
                                 for j := 0; j < gradoSecciones; j++ {
                                     cuadro[i][j] = seccionA[i][j] 
                                 }
                             }
                             
                             // Copia los valores de la sección B
                             for i := gradoSecciones; i < numeroi; i++ { 
                                 for j := gradoSecciones; j < numeroi; j++ {
                                     cuadro[i][j] = seccionB[i-gradoSecciones][j-gradoSecciones] 
                                 }
                             }
                          
                             // Copia los valores de la sección C
                             for i := 0; i < gradoSecciones; i++ { 
                                 for j := gradoSecciones; j < numeroi; j++ {
                                     cuadro[i][j] = seccionC[i][j-gradoSecciones] 
                                 }
                             }
                            
                             // Copia los valores de la sección D
                             for i := gradoSecciones; i < numeroi; i++ { 
                                 for j := 0; j < gradoSecciones; j++ {
                                     cuadro[i][j] = seccionD[i-gradoSecciones][j] 
                                 }
                             }
		case "DOBLE PARIDAD":
                        /* Se deben crear en la matriz 2 secciones de n/4 filas x n/2 columnas
                           en donde n es el grado del cuadrado mágico (si es de 4x4, el grado es 4, si es de 8x8 el grado es 8).
                           La primera sección abarca el centro de las filas superiores y la segunda sección
                           ocupa el centro de las filas inferiores.
                           Si tenemos un cuadrado mágico de 8x8 (filas de la 0 a la 7 y columnas de la 0 a la 7),
                           estas secciones serían de 2x4.
                           La sección de la parte superior ocupa las casillas 0,2 a la 0,5 y las casillas 1,2 a la 1,5.
                           La sección de la parte inferior ocupa las casillas 6,2 a la 6,5 y las casillas 7,2 a la 7,5.
                           
                           Debemos crear en la matriz otras 2 secciones ahora de n/2 filas x n/4 columnas
                           en donde n es el grado del cuadrado mágico (si es de 4x4, el grado es 4, si es de 8x8 el grado es 8).
                           La primera sección abarca el centro de las columnas más a la izauiqrda y la segunda sección
                           ocupa el centro de las columnas más a la derecha.
                           Si tenemos un cuadrado mágico de 8x8 (filas de la 0 a la 7 y columnas de la 0 a la 7),
                           estas secciones serían de 4x2.
                           La sección de la parte izquierda ocupa las casillas 2,0 a la 5,0 y las casillas 2,1 a la 5,1.
                           La sección de la parte derecha ocupa las casillas 2,6 a la 5,6 y las casillas 2,7 a la 5,7. 
	                   
                           Para identificar las casillas que pertenecen a cualquiera de las 4 secciones, las vamos a marcar
                           asignándoles el valor -1. 
                         */
                         filasH := numeroi/4
                         columnasH := numeroi/2
                         marcaSeccionesHorizontales(cuadro, filasH, columnasH, numeroi)
                         filasV := numeroi/2
                         columnasV := numeroi/4
                         marcaSeccionesVerticales(cuadro, filasV, columnasV, numeroi)
                         
                         /* Después, recorriendo la matriz de izquierda a derecha empezando en la
                            esquina superior izquierda simulamos ir llenando las casillas con la serie de números del 1 en
                            adelante pero sólo guardamos los números en aquellas casillas marcadas con -1. Siguiendo el
                            ejemplo del cuadro mágico de 8x8, el número uno iría en la casilla 0,0, pero como esa casilla no
                            contiene un -1 (no pertenece a ninguna de las 4 secciones mencionadas al principio), no guardamos
                            el uno, pasamos a la siguiente casilla que es la 0,1, ahí tendría que ir el número dos, pero como
                            tampoco contiene un -1, no lo guardamos; seguimos a la casilla 0,2 a donde corresponde poner el 
                            número tres. Esta casilla si tiene un -1 (si pertenece a una de las 4 secciones mencionadas al principio)
                            así que guardamos en esta casilla el número que corresponde (el 3). Y así continuamos sólo
                            guardando los números correspondientes en las casillas que contengan un -1.
                         */
                         elemento := 0
                         for i := 0; i < numeroi; i++ { 
                             for j := 0; j < numeroi; j++ {
                                 if (cuadro[i][j] == -1) {
                                     cuadro[i][j] = serie[elemento]
                                 }
                             elemento++
                             }
                         }
                         
                         /* Una vez que ya se recorrió la matriz completa y sólo se guardaron los números de la serie en las
                            casillas correspondientes a alguna de las 4 secciones, recorremos una vez más la matriz de izquierda a
                            derecha empezando en la esquina superior izquierda llenando las casillas que no se llenaron en el
                            recorrido anterior (tienen 0) con los números de la serie pero en orden descendente. En nuestro
                            ejemplo del cuadrado mágico de 8x8, en la casilla 0,0 pondráimos el número 64, en la casilla 0,1 el
                            número 63, como la casilla 0,2 no está vacía (ya está el número 3), no escribimos el número 62 que es
                            el número que sigue en la serie.
                            Y siguiendo el mismo procedimiento, la próxima casilla vacía sería la 0,6 en donde deberemos guardar el
                            número 58 y así sucesivamente.
                         */
                         elemento = (numeroi*numeroi)-1
                         for i := 0; i < numeroi; i++ { 
                             for j := 0; j < numeroi; j++ {
                                 if (cuadro[i][j] == 0) {
                                     cuadro[i][j] = serie[elemento]
                                 }
                             elemento--
	                     }
	                 }
        }
	    
        // Imprime el cuadrado mágico
        imprimeMatriz(cuadro)
    }
}

// Crea la matriz de nxn
func creaMatriz(n int) [][]int {
    matriz := make([][]int, n)
    filas := make([]int, n*n)
    for i := 0; i < n; i++ {
        matriz[i] = filas[i*n : (i+1)*n]
    }
    return matriz
}

// Regresa un arreglo de enteros de uno hasta elementos
func generaSerie(elementos int) []int {
    arreglo := make([]int, elementos)
    
    for i := range arreglo {
        arreglo[i] = i + 1
    }
    return arreglo
}

// Imprime la matriz que recibe como parámetro
func imprimeMatriz(matriz[][] int) {
    n := len(matriz[0])
    for i := 0; i < n; i++ { 
        for j := 0; j < n; j++ {
            fmt.Printf("%3d", matriz[i][j]) 
        }
    fmt.Println()
    }
    fmt.Println()
}

/* Resuelve un cuadrado mágico impar
   Recibe como parámetro el cuadrado mágico y la serie de números que debe utilizar
*/
func resuelveCuadradoImpar(matriz[][] int, numeros[] int) {
    n := len(numeros)
    grado := int(math.Sqrt(float64(n)))
    // Pone el primer número de la seire en la casilla central de la fila superior
    centro := grado/2
    matriz[0][centro] = numeros[0]
		             
    /* A partir del segundo número de la serie, los números se van colocando
       moviendonos una casilla hacia arriba y luego una casilla a la derecha
       siguiendo las siguientes reglas:
          1. Si un movimiento lleva a una casilla por encima de la fila superior del cuadrado mágico,
             permanece en esa columna, pero se ubica en la fila inferior de dicha columna.
          2. Si el movimiento lleva a una casilla fuera del límite derecho del cuadrado mágico,
             permanece en la fila de dicha casilla, pero se ubica en la columna más a la izquierda de esa fila.
          3. Si el movimiento lleva a una casilla que ya está ocupada, regresa a la última casilla que
             se llenó y se ubica debajo.
    */
    filaActual := 0
    columnaActual := centro
    
    for i := 1; i < len(numeros); i++ {
        arriba := filaActual-1
        if (arriba < 0) {
            arriba = grado-1
        }
                          
        derecha := columnaActual+1
        if (derecha > grado-1) {
            derecha = 0
        }
                          
        // Checa si la casilla propuesta para colocar el número en turno no está ocupada
        if (matriz[arriba][derecha] == 0) {
            filaActual = arriba
            columnaActual = derecha
        } else if (filaActual < grado-1) {
                   filaActual++
        } else {
                filaActual = 0
        }
        matriz[filaActual][columnaActual] = numeros[i]
    }
}

func marcaSeccionesHorizontales(matriz[][] int, numFilas int, numColumnas int, gradoCuadrado int) {  
    columnaInicial := (gradoCuadrado-numColumnas)/2
    columnaFinal := (numColumnas+columnaInicial)-1
    
    // Pone -1 en todas las casillas que corresponderán a la sección superior
    for i := 0; i < numFilas; i++ { 
        for j := columnaInicial; j <= columnaFinal; j++ {
            matriz[i][j] = -1
        }
    }
    // Pone -1 en todas las casillas que corresponderán a la sección inferior
    for i := gradoCuadrado-numFilas; i < gradoCuadrado; i++ { 
        for j := columnaInicial; j <= columnaFinal; j++ {
            matriz[i][j] = -1
        }
    }
}  

func marcaSeccionesVerticales(matriz[][] int, numFilas int, numColumnas int, gradoCuadrado int) {  
    filaInicial := (gradoCuadrado-numFilas)/2
    filaFinal := (numFilas+filaInicial)-1
    
    // Pone -1 en todas las casillas que corresponderán a la sección izquierda
    for i := filaInicial; i <= filaFinal; i++ { 
        for j := 0; j < numColumnas; j++ {
            matriz[i][j] = -1
        }
    }
    // Pone -1 en todas las casillas que corresponderán a la sección derecha
    for i := filaInicial; i <= filaFinal; i++ { 
        for j := gradoCuadrado-numColumnas; j < gradoCuadrado; j++ {
            matriz[i][j] = -1
        }
    }
}

Aquí una captura de pantalla de una ejecución del programa