Magic squares of doubly even order: Difference between revisions
→{{header|Haskell}}: Adding comments, and a shorter route where order N is a power of 2 |
|||
Line 309: | Line 309: | ||
----------------------------------------------------------------------- |
|||
magicSeries :: Int -> [Bool] |
magicSeries :: Int -> [Bool] |
Revision as of 15:12, 10 December 2016
You are encouraged to solve this task according to the task description, using any language you may know.
A magic square is an NxN square matrix whose numbers consist of consecutive numbers arranged so that the sum of each row and column, and both diagonals are equal to the same sum (which is called the magic number or magic constant).
A magic square of doubly even order has a size that is a multiple of four (e.g. 4, 8, 12). This means that the subsquares also have an even size, which plays a role in the construction.
1 | 2 | 62 | 61 | 60 | 59 | 7 | 8 |
9 | 10 | 54 | 53 | 52 | 51 | 15 | 16 |
48 | 47 | 19 | 20 | 21 | 22 | 42 | 41 |
40 | 39 | 27 | 28 | 29 | 30 | 34 | 33 |
32 | 31 | 35 | 36 | 37 | 38 | 26 | 25 |
24 | 23 | 43 | 44 | 45 | 46 | 18 | 17 |
49 | 50 | 14 | 13 | 12 | 11 | 55 | 56 |
57 | 58 | 6 | 5 | 4 | 3 | 63 | 64 |
- Task
Create a magic square of 8 x 8.
- Related tasks
- See also
C++
<lang cpp>#include <iostream>
- include <sstream>
- include <iomanip>
using namespace std;
class magicSqr { public:
magicSqr( int d ) { while( d % 4 > 0 ) { d++; } sz = d; sqr = new int[sz * sz]; fillSqr(); } ~magicSqr() { delete [] sqr; }
void display() const { cout << "Doubly Even Magic Square: " << sz << " x " << sz << "\n"; cout << "It's Magic Sum is: " << magicNumber() << "\n\n"; ostringstream cvr; cvr << sz * sz; int l = cvr.str().size(); for( int y = 0; y < sz; y++ ) { int yy = y * sz; for( int x = 0; x < sz; x++ ) { cout << setw( l + 2 ) << sqr[yy + x]; } cout << "\n"; } cout << "\n\n"; }
private:
void fillSqr() { static const bool tempAll[4][4] = {{ 1, 0, 0, 1 }, { 0, 1, 1, 0 }, { 0, 1, 1, 0 }, { 1, 0, 0, 1 } }; int i = 0; for( int curRow = 0; curRow < sz; curRow++ ) { for( int curCol = 0; curCol < sz; curCol++ ) { sqr[curCol + sz * curRow] = tempAll[curRow % 4][curCol % 4] ? i + 1 : sz * sz - i; i++; } } } int magicNumber() const { return sz * ( ( sz * sz ) + 1 ) / 2; } int* sqr; int sz;
};
int main( int argc, char* argv[] ) {
magicSqr s( 8 ); s.display(); return 0;
}</lang>
- Output:
Doubly Even Magic Square: 8 x 8 It's Magic Sum is: 260 1 63 62 4 5 59 58 8 56 10 11 53 52 14 15 49 48 18 19 45 44 22 23 41 25 39 38 28 29 35 34 32 33 31 30 36 37 27 26 40 24 42 43 21 20 46 47 17 16 50 51 13 12 54 55 9 57 7 6 60 61 3 2 64
Elixir
<lang elixir>defmodule Magic_square do
def doubly_even(n) when rem(n,4)!=0, do: raise ArgumentError, "must be even, but not divisible by 4." def doubly_even(n) do n2 = n * n Enum.zip(1..n2, make_pattern(n)) |> Enum.map(fn {i,p} -> if p, do: i, else: n2 - i + 1 end) |> Enum.chunk(n) |> to_string(n) |> IO.puts end defp make_pattern(n) do pattern = Enum.reduce(1..4, [true], fn _,acc -> acc ++ Enum.map(acc, &(!&1)) end) |> Enum.chunk(4) for i <- 0..n-1, j <- 0..n-1, do: Enum.at(pattern, rem(i,4)) |> Enum.at(rem(j,4)) end defp to_string(square, n) do format = String.duplicate("~#{length(to_char_list(n*n))}w ", n) <> "\n" Enum.map_join(square, fn row -> :io_lib.format(format, row) end) end
end
Magic_square.doubly_even(8)</lang>
- Output:
1 63 62 4 5 59 58 8 56 10 11 53 52 14 15 49 48 18 19 45 44 22 23 41 25 39 38 28 29 35 34 32 33 31 30 36 37 27 26 40 24 42 43 21 20 46 47 17 16 50 51 13 12 54 55 9 57 7 6 60 61 3 2 64
FreeBASIC
<lang freebasic>' version 18-03-2016 ' compile with: fbc -s console ' doubly even magic square 4, 8, 12, 16...
Sub Err_msg(msg As String)
Print msg Beep : Sleep 5000, 1 : Exit Sub
End Sub
Sub de_magicsq(n As UInteger, filename As String = "")
' filename <> "" then save square in a file ' filename can contain directory name ' if filename exist it will be overwriten, no error checking
If n < 4 Then Err_msg( "Error: n is to small") Exit Sub End If
If (n Mod 4) <> 0 Then Err_msg "Error: not possible to make doubly" + _ " even magic square size " + Str(n) Exit Sub End If
Dim As UInteger sq(1 To n, 1 To n) Dim As UInteger magic_sum = n * (n ^ 2 +1) \ 2 Dim As UInteger q = n \ 4 Dim As UInteger x, y, nr = 1 Dim As String frmt = String(Len(Str(n * n)) +1, "#")
' set up the square For y = 1 To n For x = q +1 To n - q sq(x,y) = 1 Next Next For x = 1 To n For y = q +1 To n - q sq(x, y) Xor= 1 Next Next
' fill the square q = n * n +1 For y = 1 To n For x = 1 To n If sq(x,y) = 0 Then sq(x,y) = q - nr Else sq(x,y) = nr End If nr += 1 Next Next
' check columms and rows For y = 1 To n nr = 0 : q = 0 For x = 1 To n nr += sq(x,y) q += sq(y,x) Next If nr <> magic_sum Or q <> magic_sum Then Err_msg "Error: value <> magic_sum" Exit Sub End If Next
' check diagonals nr = 0 : q = 0 For x = 1 To n nr += sq(x, x) q += sq(n - x +1, n - x +1) Next If nr <> magic_sum Or q <> magic_sum Then Err_msg "Error: value <> magic_sum" Exit Sub End If
' printing square's on screen bigger when ' n > 19 results in a wrapping of the line Print "Single even magic square size: "; n; "*"; n Print "The magic sum = "; magic_sum Print For y = 1 To n For x = 1 To n Print Using frmt; sq(x, y); Next Print Next
' output magic square to a file with the name provided If filename <> "" Then nr = FreeFile Open filename For Output As #nr Print #nr, "Single even magic square size: "; n; "*"; n Print #nr, "The magic sum = "; magic_sum Print #nr, For y = 1 To n For x = 1 To n Print #nr, Using frmt; sq(x,y); Next Print #nr, Next Close #nr End If
End Sub
' ------=< MAIN >=------
de_magicsq(8, "magic8de.txt") : Print
' empty keyboard buffer While Inkey <> "" : Wend Print : Print "hit any key to end program" Sleep End</lang>
- Output:
Single even magic square size: 8*8 The magic sum = 260 64 63 3 4 5 6 58 57 56 55 11 12 13 14 50 49 17 18 46 45 44 43 23 24 25 26 38 37 36 35 31 32 33 34 30 29 28 27 39 40 41 42 22 21 20 19 47 48 16 15 51 52 53 54 10 9 8 7 59 60 61 62 2 1
Haskell
<lang Haskell>import Data.List (transpose)
magicSquare :: Int -> Int magicSquare n
| (rem n 4) > 0 = [] | otherwise = splitEvery n $ -- Taken directly from the integer series where True -- and from the reverse of that series where False zipWith (\x i -> if x then i else limit - i) series [1..sqr] where sqr = n * n limit = sqr + 1 series -- For powers of 2, the (append not) 'magic' series directly -- yields the truth table that we need | isPowerOf 2 n = magicSeries $ floor (logBase 2 (fromIntegral sqr)) + 1 -- where n is not a power of 2, we can replicate a -- minimum truth table, horizontally and vertically | otherwise = (concat $ concat $ concat $ scale $ fmap scale $ splitEvery 4 $ magicSeries 5) where scale = replicate $ quot n 4
magicSeries :: Int -> [Bool] magicSeries n
| n <= 1 = [True] | otherwise = xs ++ (not <$> xs) where xs = magicSeries (n - 1)
splitEvery :: Int -> [a] -> a splitEvery n xs
| length xs <= n = [xs] | otherwise = [gp] ++ splitEvery n t where (gp, t) = splitAt n xs
isPowerOf :: Int -> Int -> Bool isPowerOf k n =
(until (\x -> (rem x k) /= 0) (\x -> quot x k ) n) == 1
main :: IO () main = mapM_ (putStrLn . show) $ magicSquare 8
-- Summed and checked checked :: Int -> (Int, Bool) checked n = (h, all (\x -> h == x) t)
where square = magicSquare n sums@h:t = sum <$> (square) ++ -- rows (transpose square) ++ -- cols (diagonals square) -- diagonals
diagonals :: Int -> Int diagonals xs =
map (\x -> zipWith (!!) x [0..]) [xs, reverse xs]
main2 :: IO () main2 = putStrLn $ show (checked 8)</lang>
- Output:
main [1,63,62,4,5,59,58,8] [56,10,11,53,52,14,15,49] [48,18,19,45,44,22,23,41] [25,39,38,28,29,35,34,32] [33,31,30,36,37,27,26,40] [24,42,43,21,20,46,47,17] [16,50,51,13,12,54,55,9] [57,7,6,60,61,3,2,64] main2 (260,True)
Java
<lang java>public class MagicSquareDoublyEven {
public static void main(String[] args) { int n = 8; for (int[] row : magicSquareDoublyEven(n)) { for (int x : row) System.out.printf("%2s ", x); System.out.println(); } System.out.printf("\nMagic constant: %d ", (n * n + 1) * n / 2); }
static int[][] magicSquareDoublyEven(final int n) { if (n < 4 || n % 4 != 0) throw new IllegalArgumentException("base must be a positive " + "multiple of 4");
// pattern of count-up vs count-down zones int bits = 0b1001_0110_0110_1001; int size = n * n; int mult = n / 4; // how many multiples of 4
int[][] result = new int[n][n];
for (int r = 0, i = 0; r < n; r++) { for (int c = 0; c < n; c++, i++) { int bitPos = c / mult + (r / mult) * 4; result[r][c] = (bits & (1 << bitPos)) != 0 ? i + 1 : size - i; } } return result; }
}</lang>
1 2 62 61 60 59 7 8 9 10 54 53 52 51 15 16 48 47 19 20 21 22 42 41 40 39 27 28 29 30 34 33 32 31 35 36 37 38 26 25 24 23 43 44 45 46 18 17 49 50 14 13 12 11 55 56 57 58 6 5 4 3 63 64 Magic constant: 260
JavaScript
ES6
<lang JavaScript>(() => {
'use strict';
// doubleEvenMagicSquare :: Int -> Int const doubleEvenMagicSquare = n => { if (n % 4 > 0) return undefined;
// truthSeries :: Int -> [Int] const truthSeries = n => { if (n <= 1) return [true]; const xs = truthSeries(n - 1); return xs.concat(xs.map(x => !x)); };
const sqr = n * n, scale = curry(replicate)(n / 4), power = Math.log2(sqr), sequence = isInt(power) ? truthSeries(power + 1) : ( flatten(scale(splitEvery(4, truthSeries(5)) .map(scale))) );
return splitEvery(n, sequence .map((x, i) => x ? i + 1 : sqr - i)); };
// GENERIC FUNCTIONS ----------------------------------------------------
// flatten :: Tree a -> [a] const flatten = t => (t instanceof Array ? concatMap(flatten, t) : [t]);
// concatMap :: (a -> [b]) -> [a] -> [b] const concatMap = (f, xs) => [].concat.apply([], xs.map(f));
// splitEvery :: Int -> [a] -> [][a]] const splitEvery = (n, xs) => { if (xs.length <= n) return [xs]; const [h, t] = [xs.slice(0, n), xs.slice(n)]; return [h].concat(splitEvery(n, t)); }
// curry :: ((a, b) -> c) -> a -> b -> c const curry = f => a => b => f(a, b);
// replicate :: Int -> a -> [a] const replicate = (n, a) => { let v = [a], o = []; if (n < 1) return o; while (n > 1) { if (n & 1) o = o.concat(v); n >>= 1; v = v.concat(v); } return o.concat(v); };
// isInt :: Int -> Bool const isInt = x => x === Math.floor(x);
// TEST AND DISPLAY FUNCTIONS -------------------------------------------
// transpose :: a -> a const transpose = xs => xs[0].map((_, iCol) => xs.map((row) => row[iCol]));
// diagonals :: a -> ([a], [a]) const diagonals = xs => { const nRows = xs.length, nCols = (nRows > 0 ? xs[0].length : 0); const cell = (x, y) => xs[y][x];
if (nRows === nCols) { const ns = range(0, nCols - 1); return [zipWith(cell, ns, ns), zipWith(cell, ns, reverse(ns))]; } else return [ [], [] ]; };
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] const zipWith = (f, xs, ys) => { const ny = ys.length; return (xs.length <= ny ? xs : xs.slice(0, ny)) .map((x, i) => f(x, ys[i])); }
// reverse :: [a] -> [a] const reverse = (xs) => xs.slice(0) .reverse()
// range :: Int -> Int -> [Int] const range = (m, n) => Array.from({ length: Math.floor(n - m) + 1 }, (_, i) => m + i);
// all :: (a -> Bool) -> [a] -> Bool const all = (f, xs) => xs.every(f);
// show :: a -> String const show = x => JSON.stringify(x);
// justifyRight :: Int -> Char -> Text -> Text const justifyRight = (n, cFiller, strText) => n > strText.length ? ( (cFiller.repeat(n) + strText) .slice(-n) ) : strText;
// TEST ----------------------------------------------------------------- return [4, 8, 12] .map(n => { const lines = doubleEvenMagicSquare(n); const sums = lines.concat( transpose(lines) .concat(diagonals(lines)) ) .map(xs => xs.reduce((a, b) => a + b, 0)); const sum = sums[0]; return [ "Order: " + n.toString(), "Summing to: " + sum.toString(), "Row, column and diagonal sums checked: " + all(x => x === sum, sums) .toString() + '\n', lines.map( xs => xs.map( x => justifyRight(3, ' ', x.toString()) ) .join(' ')) .join('\n') ].join('\n') }) .join('\n\n');
})();</lang>
- Output:
Order: 4 Summing to: 34 Row, column and diagonal sums checked: true 1 15 14 4 12 6 7 9 8 10 11 5 13 3 2 16 Order: 8 Summing to: 260 Row, column and diagonal sums checked: true 1 63 62 4 60 6 7 57 56 10 11 53 13 51 50 16 48 18 19 45 21 43 42 24 25 39 38 28 36 30 31 33 32 34 35 29 37 27 26 40 41 23 22 44 20 46 47 17 49 15 14 52 12 54 55 9 8 58 59 5 61 3 2 64 Order: 12 Summing to: 870 Row, column and diagonal sums checked: true 1 143 142 4 5 139 138 8 9 135 134 12 132 14 15 129 128 18 19 125 124 22 23 121 120 26 27 117 116 30 31 113 112 34 35 109 37 107 106 40 41 103 102 44 45 99 98 48 49 95 94 52 53 91 90 56 57 87 86 60 84 62 63 81 80 66 67 77 76 70 71 73 72 74 75 69 68 78 79 65 64 82 83 61 85 59 58 88 89 55 54 92 93 51 50 96 97 47 46 100 101 43 42 104 105 39 38 108 36 110 111 33 32 114 115 29 28 118 119 25 24 122 123 21 20 126 127 17 16 130 131 13 133 11 10 136 137 7 6 140 141 3 2 144
PARI/GP
A magic one-liner: <lang parigp>magicsquare(n)=matrix(n,n,i,j,k=i+j*n-n;if(bitand(38505,2^((j-1)%4*4+(i-1)%4)),k,n*n+1-k))</lang>
Output:
magicsquare(8) [ 1 56 48 25 33 24 16 57] [63 10 18 39 31 42 50 7] [62 11 19 38 30 43 51 6] [ 4 53 45 28 36 21 13 60] [ 5 52 44 29 37 20 12 61] [59 14 22 35 27 46 54 3] [58 15 23 34 26 47 55 2] [ 8 49 41 32 40 17 9 64]
BTW: The bit field number 38505 = 9669h seems to come from hell to do the magic...
Perl 6
See Magic squares/Perl 6 for a general magic square generator.
- Output:
With a parameter of 8:
1 2 62 61 60 59 7 8 9 10 54 53 52 51 15 16 48 47 19 20 21 22 42 41 40 39 27 28 29 30 34 33 32 31 35 36 37 38 26 25 24 23 43 44 45 46 18 17 49 50 14 13 12 11 55 56 57 58 6 5 4 3 63 64 The magic number is 260
With a parameter of 12:
1 2 3 141 140 139 138 137 136 10 11 12 13 14 15 129 128 127 126 125 124 22 23 24 25 26 27 117 116 115 114 113 112 34 35 36 108 107 106 40 41 42 43 44 45 99 98 97 96 95 94 52 53 54 55 56 57 87 86 85 84 83 82 64 65 66 67 68 69 75 74 73 72 71 70 76 77 78 79 80 81 63 62 61 60 59 58 88 89 90 91 92 93 51 50 49 48 47 46 100 101 102 103 104 105 39 38 37 109 110 111 33 32 31 30 29 28 118 119 120 121 122 123 21 20 19 18 17 16 130 131 132 133 134 135 9 8 7 6 5 4 142 143 144 The magic number is 870
REXX
Marked numbers indicate that those (sequentially generated) numbers don't get swapped (and thusly, stay in place in the magic square). <lang rexx>/*REXX program constructs a magic square of doubly even sides (a size divisible by 4).*/ n=8; s=n%4; L=n%2-s+1; w=length(n**2) /*size; small sq; low middle; # width*/ @.=0; H=n%2+s /*array default; high middle. */ call gen /*generate a grid in numerical order. */ call diag /*mark numbers on both diagonals. */ call corn /* " " in small corner boxen. */ call midd /* " " in the middle " */ call swap /*swap positive numbers with highest #.*/ call show /*display the doubly even magic square.*/ call sum /* " " magic number for square. */ exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ o: parse arg ?; return n-?+1 /*calculate the "other" (right) column.*/ @: parse arg x,y; return abs(@.x.y) diag: do r=1 for n; @.r.r=-@(r,r); o=o(r); @.r.o=-@(r,o); end; return midd: do r=L to H; do c=L to H; @.r.c=-@(r,c); end; end; return gen: #=0; do r=1 for n; do c=1 for n; #=#+1; @.r.c=#; end; end; return show: #=0; do r=1 for n; $=; do c=1 for n; $=$ right(@(r,c),w); end; say $; end; return sum: #=0; do r=1 for n; #=#+@(r,1); end; say; say 'The magic number is: ' #; return max#: do a=n to 1 by -1; do b=n to 1 by -1; if @.a.b>0 then return; end; end /*──────────────────────────────────────────────────────────────────────────────────────*/ swap: do r=1 for n
do c=1 for n; if @.r.c<0 then iterate; call max# /*find max number.*/ parse value -@.a.b (-@.r.c) with @.r.c @.a.b /*swap two values.*/ end /*c*/ end /*r*/ return
/*──────────────────────────────────────────────────────────────────────────────────────*/ corn: do r=1 for n; if r>s & r<=n-s then iterate /*"corner boxen", size≡S*/
do c=1 for n; if c>s & c<=n-s then iterate; @.r.c=-@(r,c); end /*c*/ end /*r*/ return</lang>
output when using the default input:
1 2 62 61 60 59 7 8 9 10 54 53 52 51 15 16 48 47 19 20 21 22 42 41 40 39 27 28 29 30 34 33 32 31 35 36 37 38 26 25 24 23 43 44 45 46 18 17 49 50 14 13 12 11 55 56 57 58 6 5 4 3 63 64 The magic number is: 260
Ruby
<lang ruby>def double_even_magic_square(n)
raise ArgumentError, "Need multiple of four" if n%4 > 0 block_size, max = n/4, n*n pre_pat = [true, false, false, true, false, true, true, false] pre_pat += pre_pat.reverse pattern = pre_pat.flat_map{|b| [b] * block_size} * block_size flat_ar = pattern.each_with_index.map{|yes, num| yes ? num+1 : max-num} flat_ar.each_slice(n).to_a
end
def to_string(square)
n = square.size fmt = "%#{(n*n).to_s.size + 1}d" * n square.inject(""){|str,row| str << fmt % row << "\n"}
end
puts to_string(double_even_magic_square(8))</lang>
- Output:
1 2 62 61 60 59 7 8 56 55 11 12 13 14 50 49 48 47 19 20 21 22 42 41 25 26 38 37 36 35 31 32 33 34 30 29 28 27 39 40 24 23 43 44 45 46 18 17 16 15 51 52 53 54 10 9 57 58 6 5 4 3 63 64
zkl
<lang zkl>class MagicSquareDoublyEven{
fcn init(n){ var result=magicSquareDoublyEven(n) } fcn toString{ sink,n:=Sink(String),result.len(); // num collumns fmt:="%2s "; foreach row in (result) { sink.write(row.apply('wrap(n){ fmt.fmt(n) }).concat(),"\n") } sink.write("\nMagic constant: %d".fmt((n*n + 1)*n/2)); sink.close(); } fcn magicSquareDoublyEven(n){ if (n<4 or n%4!=0 or n>16)
throw(Exception.ValueError("base must be a positive multiple of 4"));
bits,size,mult:=0b1001011001101001, n*n, n/4; result:=n.pump(List(),n.pump(List(),0).copy); // array[n,n] of zero
foreach i in (size){
bitsPos:=(i%n)/mult + (i/(n*mult)*4); value:=(bits.bitAnd((2).pow(bitsPos))) and i+1 or size-i; result[i/n][i%n]=value;
} result; }
} MagicSquareDoublyEven(8).println();</lang>
- Output:
1 2 62 61 60 59 7 8 9 10 54 53 52 51 15 16 48 47 19 20 21 22 42 41 40 39 27 28 29 30 34 33 32 31 35 36 37 38 26 25 24 23 43 44 45 46 18 17 49 50 14 13 12 11 55 56 57 58 6 5 4 3 63 64 Magic constant: 260