Engee documentation
Notebook

Magic square with odd-sized sides

A magic square of size $N \times N$ is a matrix filled with integers from 1 to $N^2$ such that the sum of the numbers along each row, each column and the main diagonals is the same and equals $N(N^2 + 1)/2$.

Algorithm for solving the problem

Siamese method of filling a magic square of size $N$ (where $N$ is an odd number) consists of the following steps:

  1. Select a cell in the middle of the top row, accept the cell $n=1$
  2. Place $n$ in the selected cell
  3. If $n = N^2$, then the square is filled and you can exit the procedure. Otherwise increase $n$ by one
  4. Move to the cell above and to the right of the current cell. Move to the first column or to the last row when moving outside the matrix. If the new cell is already filled with a non-zero number, return and shift 1 cell lower (vertically) instead.
  5. Return to step 2
In [ ]:
Pkg.add(["Colors", "LinearAlgebra"])
In [ ]:
N = 7

magic_square = Matrix{Int}(zeros(N,N))

n = 1
i = 1
j = N ÷ 2

while n <= N^2
    magic_square[i, j] = n
    n += 1
    newi, newj = mod1((i-1), N), mod1((j+1), N)
    if magic_square[newi, newj] > 0
        i = mod1( i+1, N )
    else
        i, j = newi, newj
    end
end

display( magic_square )
7×7 Matrix{Int64}:
 39  48   1  10  19  28  30
 47   7   9  18  27  29  38
  6   8  17  26  35  37  46
 14  16  25  34  36  45   5
 15  24  33  42  44   4  13
 23  32  41  43   3  12  21
 31  40  49   2  11  20  22
In [ ]:
gr()
heatmap( magic_square, c=:Greens, size=(200,200), cbar=:false,
         xticks=false, yticks=false )

# Вычислим координаты, текст и цвет надписей
vec_coords = CartesianIndices( size(magic_square) )
ax = first.( Tuple.(vec_coords[:]) )
ay = last.( Tuple.(vec_coords[:]) )
az = reshape( magic_square, 1, : )
acol = [ n > (N^2)÷2 ? (:white) : (:black) for n in az ]

# Нанесем на график надписи
for (x,y,z,c) in zip(ax,ay,az,acol)
    annotate!( x, y, text("$(convert(Int64, round(z)))",9,"Helvetica",c) );
end
plot!()
Out[0]:
39 47 6 14 15 23 31 48 7 8 16 24 32 40 1 9 17 25 33 41 49 10 18 26 34 42 43 2 19 27 35 36 44 3 11 28 29 37 45 4 12 20 30 38 46 5 13 21 22

Let's check that we got exactly the magic square. Here is the sum of all columns:

In [ ]:
sum.( eachcol( magic_square ) )
Out[0]:
7-element Vector{Int64}:
 175
 175
 175
 175
 175
 175
 175

The sum of all rows:

In [ ]:
sum.( eachrow( magic_square ) )
Out[0]:
7-element Vector{Int64}:
 175
 175
 175
 175
 175
 175
 175

Sum of the numbers on the main diagonal:

In [ ]:
using LinearAlgebra
sum( diag( magic_square) )
Out[0]:
175

Sum of the numbers on the side diagonal:

In [ ]:
sum( diag( magic_square') )
Out[0]:
175

Let's check all the conditions at once:

In [ ]:
using Colors

verdict = all( vcat( sum.( eachcol( magic_square ) ), 
           sum.( eachrow( magic_square ) ),
           sum( diag( magic_square) ),
           sum( diag( magic_square') ) ) .== N*(N^2 + 1) ÷ 2 )

if verdict == true display( colorant"green" ) else display( colorant"red" ); end;
No description has been provided for this image

If we set an even value $N$, these conditions will not be fulfilled (the result will be false).

Conclusion

We have implemented a procedure for constructing a magic square with some limitations. It embodies a rather simple algorithm, and learning it is definitely useful for cultivating intuition in programming.