Cultivando cuadrados mágicos con Python

Patrón de cuadrado mágico con el lado 64 (ni un solo número se detiene :)
Patrón de cuadrado mágico con el lado 64 (ni un solo número se detiene :)

Buenos días.





En este artículo, describiré un método para obtener cuadrados mágicos normales de orden n m , donde nym son números naturales positivos, siempre que conozcamos el cuadrado mágico normal de orden n





, , , . -, . , .





, , , "".





, , , , :





  1. 1 n2, n - , , , , .





  2. , .





  3. , .





  4. , ( ). , , , - 66.





  5. , , "".





  6. , .





  7. , , , : , , , /.





, , : "" , ( ), , , . , , L , , L, , .





(n=3 n=4), , . , . , , , , 55, 66, 77 99.





, , , 66. , , , 66 . , :)





99, , : - , " ", .





, : , , , . , , , , . , , , , .





, , . , .





, , , , , . , , . 33, , "". , : , 99, 9 , , . , ?





- , . , , !





2727, , 729 1 729, . , , , , , , ( -) ... . , , ?





NumPy + PyGame = Profit!

, , . Python 3.9 opensource- Numpy PyGame.





import numpy as np
import pygame as pg
# SortedList uses binary search instead full-list comparison cycle.
import sortedcontainers as scs
      
      



- , mx,y == x + y * n + 1, n - . -:





def create_matrix(n: int):
    return np.arange(1, n ** 2 + 1).reshape(n, n)
      
      



3x3 4x4, :





m_3 = [[[1, 0], [0, 0], [1, 2], [2, 2]], [[2, 0], [2, 1], [0, 2], [0, 1]]]
m_4 = [[[0, 0], [2, 2]], [[1, 0], [0, 1]],
       [[2, 0], [3, 1]], [[3, 0], [1, 2]],
       [[0, 2], [1, 3]], [[1, 1], [3, 3]],
       [[2, 1], [0, 3]], [[3, 2], [2, 3]]]
      
      



, NumPy , , :





# _m - input matrix, _x - base row length
def m_split(_m, _x):
    return [np.vsplit(x, _x) for x in np.hsplit(_m, _x)]


# _m - input "previous splitted" matrix, _x - base row length
def m_join(_m, _x):
    return np.vstack([np.hstack(np.vstack(_m)[x::_x]) for x in range(_x)])
      
      



, , :





# _m - input "previous splitted" matrix, idxs - indices of matrix cells.
def m_rot(_m, idxs):
  	#      [-----X----][-----Y----]
    a0 = _m[idxs[0][0]][idxs[0][1]]
    for i in range(len(idxs) - 1):
        _m[idxs[i][0]][idxs[i][1]] = _m[idxs[i + 1][0]][idxs[i + 1][1]]
    _m[idxs[-1][0]][idxs[-1][1]] = a0
    return _m


#_m - input "previous splitted" matrix, _steps - pattern of digits swaps (m_3, m_r, ...)...
def magic_steps(_m, _steps):
    m0 = _m
    for step in _steps:
        m0 = m_rot(m0, step)
    return m0
      
      



, , npow:





# _mss - magic steps (m_3, m_4, or ...)
def pow_square(_root, _pow, _mss):
    m_dmag = lambda x, d: m_join(magic_steps(m_split(x, d), _mss), d)
    _p = _root ** _pow
    m0 = m_dmag(create_matrix(_p), _root)
    for i in range(1, _pow):
        m0 = m_join([[m_dmag(x, _root) for x in y] for y in m_split(m0, _root ** i)], _root ** i)
    return m0
      
      



, , PyGame, turtle, :





# New to_star function computes and draw all lines from matrix on fly.
def to_star(_m):
    dim = len(_m)
    used = scs.SortedList()
    max_sz = 980
    tp = 10	# brush transparency.
    sz = float(max_sz / dim)
    j_to_xy = lambda _j, _d: [(_j - 1) % _d, int((_j - 1) / _d)]
    # [matrix position] -> (drawing field point)
    c_to_pg = lambda _xy: (float(_xy[1] * sz + 10), float(_xy[0] * sz + 10))
    # pygame prepare:
    pg.init()
    pg.fastevent.init()
    d_sz = (max_sz + 20, max_sz + 20)
    _sc = pg.display.set_mode(d_sz)
    _sc.fill((255, 255, 255))
    pg.display.flip()
    c = pg.time.Clock()
    # start compute & draw
    for _y in range(dim):
        for events in pg.fastevent.get():
            if events.type == pg.QUIT:
                pg.quit()
                return
        sc = _sc.convert_alpha()
        sc.fill([0, 0, 0, 0])
        for _x in range(1, dim + 1):
            j = _x + _y * dim
            x, y = j_to_xy(j, dim)
            if _m[y][x] == j or _m[y][x] in used:
                continue
            a = _m[y][x]
            x, y = j_to_xy(a, dim)
            b = _m[y][x]
            used += [a]
            pg.draw.line(sc, (0, 0, 0, tp), c_to_pg([x, y]), c_to_pg(j_to_xy(b, dim)))
        _sc.blit(sc, (0, 0))
        pg.display.update()
    sc = _sc.convert_alpha()
    sc.fill([0, 0, 0, 0])
    for j in range(1, dim * dim + 1):
        x, y = j_to_xy(j, dim)
        if _m[y][x] == j:
          	# static point transparency is 50% higher than lines transparency.
            pg.draw.circle(sc, (255, 255, 255, (tp + 50) % 100), c_to_pg([x, y]), 1.5)
    _sc.blit(sc, (0, 0))
    pg.display.update()
    while True:
        c.tick(60)
        for events in pg.event.get():
            if events.type == pg.QUIT:
                pg.quit()
                return
        pg.display.update()
      
      



- main



, , :





if __name__ == '__main__':
    pow = 3
    root = 4
    m1 = pow_square(root, pow, m_4)
    try:
        to_star(m1)
    except pygame.error:
        print('Drawing finished.')
    print(m1)
    # Compute sums of all digits in rows, columns and diagonals.
    m2 = np.rot90(m1)
    print([sum(x) for x in m1])
    print([sum(x) for x in m2])
    print(sum([m1[x, x] for x in range(root ** pow)]))
    print(sum([m2[x, x] for x in range(root ** pow)]))
      
      



, . - 33, 44, 55, , , . 3n, 4n 5n , (, - )!





El cuadrado mágico es del orden de 256. Las columnas y filas se mezclan.
256. .
Otro cuadrado mágico de orden 256. Las columnas y filas se mezclan.
256. .
El cuadrado mágico es de aproximadamente 125. Las columnas y filas se mezclan.  Tenga en cuenta: solo dos líneas rectas largas se destacan sobre el fondo general.  Esto significa que el resto de las líneas no se superponen ni se superponen.
125. . : . , , .
Cuadrado mágico de orden 81 en MS Excel.
81 MS Excel.

81 (.csv)





GitHub





: > 1000. . .





PD: Lo más probable es que el código presentado en el artículo tenga una gran cantidad de puntos de optimización y, probablemente, la matriz deba procesarse multiproceso, pero dado que este código prácticamente no tiene otro campo de aplicación, excepto demostrar el método para generar cuadrados mágicos. , se deja como está. Disculpas de antemano por alguna falta de estética en el código ...





UPD1. Las pruebas de campo han demostrado que el método es aplicable solo a cuadrados mágicos asociativos. La verificación se llevó a cabo solo en cuadrados mágicos normales. El código de este artículo se ha actualizado con fines de optimización. Los cuadrados mágicos 2048x2048 y 2401x2401 se formaron con éxito.





2401x2401
2401x2401



All Articles