Growing Magic Squares with Python

Magic square pattern with side 64 (not a single number stands still :)
Magic square pattern with side 64 (not a single number stands still :)

Good day.





In this article, I will describe a method for obtaining normal magic squares of order n m , where n and m are positive natural numbers, provided that we know the normal magic square of order 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 , (, - )!





The magic square is of the order of 256. Columns and rows are shuffled.
256. .
Another magic square of order 256. Columns and rows are shuffled.
256. .
The magic square is about 125. Columns and rows are shuffled.  Please note: only two long straight lines stand out against the general background.  This means that the rest of the lines do not go over each other, and do not overlap.
125. . : . , , .
Magic square of order 81 in MS Excel.
81 MS Excel.

81 (.csv)





GitHub





: > 1000. . .





PS Most likely, the code presented in the article has a huge number of optimization points, and, probably, the matrix should be processed multithreaded, but since this code has practically no other field of application, except for demonstrating the method for generating magic squares, it is left as it is. Apologies in advance for some lack of aesthetics in the code ...





UPD1. Field tests have shown that the method is applicable only to associative magic squares. The check was carried out only on normal magic squares. The code in this article has been updated for optimization purposes. Magic squares 2048x2048 and 2401x2401 were successfully formed.





2401x2401
2401x2401



All Articles