Jorge Martinez

Aerospace Engineer and Senior Software Developer


Generando cuadrados mágicos

Jorge Martínez Garrido

February 12, 2022

python


Se entiende por cuadrado mágico aquella matriz de tamaño $n \times n$ que presenta la propiedad de que sus filas, columnas y diagonales suman lo mismo. Dicha suma se conoce con el nombre de número mágico y se representa por la letra $M$.

Los cuadrados mágicos han sido asociados a temas esotéricos desde que se tiene conocimiento de los mismos y pueden encontrarse en numerosas construcciones, tal y como sucede en la Sagrada Familia:

Cuadrado mágico de la Sagrada Familia

Método de Simón de Louberre

En este post vamos a presentar un algortimo para generar cuadrados mágicos. Sin embargo, dicho algortimo solo funcionará para cuadrados mágicos impares, es decir, para valores de $n$ tal que $n=3$, $n=5$, $n=7$. De forma general, esto se escribe como $n=2i + 1$, siendo $i$ cualquier número natural.

El algoritmo fue desarrollado por Simón de Louberre, si bien el método ya era conocido por astrónomos orientales1. Las siguientes reglas aplican para generar los cuadrados mágicos de Loouberre:

Comenzando en la casilla central de la primera fila con el primer número, se rellena la diagonal quebrada con los siguientes en sentido NO (o NE). Completada la primera diagonal se desciende una posición y se rellena la segunda en el mismo sentido que la anterior, repitiéndose el paso anterior con el resto de diagonales hasta completar el cuadrado.

Cuadrado mágico de Louberre

Calculando el número mágico

Vamos a crear una función que calcule el nñumero mágico $M$ de un cuadrado mágico. La fórmula es la siguiente:

$$ M = \frac{n(n^2 + 1)}{2} $$

Escrita en forma de una función de Python podemos tener:

def calcular_numero_magico(n):
    """
    Calcula el número mágico M de un cuadrado mágino de tamaño nxn.
    
    Parameters
    ----------
    n: int
        Tamaño característico del cuadrado mágico.
    
    Returns
    -------
    M: int
        Número mágico.
        
    Notes
    -----
    Python aplica un type casting, por lo que debemos forzar la conversion de un flotante
    a un entero.
    """
    M = int(n * (n ** 2 + 1) / 2)
    return M

Así pues, para el caso de un cuadrado tal que $n=5$, el número mágico resulta ser de:

M = calcular_numero_magico(n=5)
print(f"El número mágico para n = 5 resulta ser {M = }")
El número mágico para n = 5 resulta ser M = 65

Algoritmo de Louberre

Quizás puede parecer algo complejo el moverse por una matriz; más aún si tenemos que hacerlo siguiendo las reglas de Louberre. Pero utilizando la geometría modular (muy aplicada para evitar los límites de una lista, cadena, matriz…) podemos generar el siguiente código:

def generar_cuadrado_magico(n):
    """
    Devuelve un cuadrado mágico de tamaño nxn.
    
    Parameters
    ----------
    n: int
        Tamaño caracterísitco del cuadrado mágico.
        
    Returns
    -------
    cuadrado_magico: list
        Cuadrado mágico en forma de listas anidadas (cada lista es una fila).
        
    Notes
    -----
    Utilizando la librería Numpy podríamos simplificar el algoritmo. No obstante, por el nivel impartido
    en clase hemos preferido utilizar aquí Python puro.
    """
    
    # Preparamos el cuadrado con 'n' filas (en forma de lista) vacias
    cuadrado_magico = [[0 for _ in range(n)] for _ in range(n)]
    
    # Comenzamos en la primera fila y en la columna central
    i, j = 0, n // 2
    
    # El primer número del cuadrado mágico será el x=1
    x = 1
    
    # Aplicamos el algoritmo
    while x < n ** 2:
        
        # Añadimos el número en la posición que le corresponde
        cuadrado_magico[i][j] = x
        x += 1
        
        # Calculamos la posición futura
        i_nueva, j_nueva = (i - 1) % n, (j + 1) % n
        
        # Comprobamos que no haya ningún número en la nueva casilla
        if cuadrado_magico[i_nueva][j_nueva]:
            i += 1
        else:
            i, j = i_nueva, j_nueva
            
    return cuadrado_magico

Siguiendo el ejemplo de $n=5$, podemos encontrar el siguiente cuadrado mágico de Louberre:

cuadrado_magico = generar_cuadrado_magico(n=5)
print(f"Cuadrado magico para n = 5\n{cuadrado_magico}")
Cuadrado magico para n = 5
[[17, 24, 1, 8, 15], [23, 5, 7, 14, 16], [4, 6, 13, 20, 22], [10, 12, 19, 21, 3], [11, 18, 0, 2, 9]]

Pretty print de un cuadrado mágico

Por el término “pretty print” se entiende a mejorar la forma en la que el resultado de un cuadrado mágico se muestra por pantalla. Para ello, vamos a crear una función llamada print_cuadrado_mágico que imprima en la consola de forma elegante nuestro cuadrado:

def print_cuadrado_magico(cuadrado_magico):
    """
    Imprime de forma elegante un cuadrado mágico.
    
    Parameters
    ----------
    cuadrado_magico: list
        Listas anidadas representando las filas de un cuadrado mágico.
    """
    
    for fila in cuadrado_magico: print(fila, end="\n")

Aplicando la función anterior conseguimos el siguiente resultado:

print_cuadrado_magico(cuadrado_magico)
[17, 24, 1, 8, 15]
[23, 5, 7, 14, 16]
[4, 6, 13, 20, 22]
[10, 12, 19, 21, 3]
[11, 18, 0, 2, 9]

Referencias