This repository has been archived by the owner on Sep 10, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #51 from stonejing/magic
add magic function
- Loading branch information
Showing
3 changed files
with
250 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
/** | ||
* @author Jing Stone | ||
* @param input number | ||
* @returns Mat object matrix | ||
* | ||
* The magic function generates a matrix that the sums of the numbers in each row, each column, | ||
* and both main diagonals are the same. | ||
* The introduction: https://en.wikipedia.org/wiki/Magic_square | ||
*/ | ||
|
||
|
||
function magic(input) | ||
{ | ||
// wrong argument number | ||
if (arguments.length === 0) { | ||
throw new Error('Exception occurred in magic - no argument given'); | ||
} | ||
else if (arguments.length > 1) { | ||
throw new Error('Exception occurred in magic - wrong argument number'); | ||
} | ||
// the input shoule be an integer | ||
if(!Number.isInteger(input)) { | ||
throw new Error("Exception occurred in magic - the paramter must be an integer."); | ||
} | ||
// 1x1 magic square is called trivial | ||
if(input === 1) return (new Mat([1])); | ||
// 2x2 magic square do not exist, retrun a default matrix | ||
if(input === 2) return (new Mat([[1, 2], [3, 4]])); | ||
|
||
/* | ||
The method of generating magic matrix depends on the rank | ||
it should be consider as odd, 4M+2, 4M, M is an integer greater than 1 | ||
mode = 0 : the input is 4M, use generate_square_doubly_even | ||
mode = 2 : the input is 4M+2, use generate_square_even | ||
mode = 1 or 3 : the input is odd, use generate_square_odd | ||
*/ | ||
let mode = input % 4; | ||
|
||
switch(mode) { | ||
case 0: | ||
let result_doubly_even = generate_square_doubly_even(input); | ||
return new Mat(result_doubly_even); | ||
case 2: | ||
let result_even = generate_square_even(input); | ||
return new Mat(result_even); | ||
case 1: | ||
case 3: | ||
let result_odd = generate_square_odd(input); | ||
return new Mat(result_odd); | ||
default: | ||
throw new Error("Exception occurred in magic - something wrong happened"); | ||
} | ||
|
||
/* | ||
https://en.wikipedia.org/wiki/Magic_square#A_method_for_constructing_a_magic_square_of_odd_order | ||
the detail of how to generate a magic matrix of odd order | ||
*/ | ||
function generate_square_odd(n) { | ||
|
||
let magicSquare = Array(n).fill(0).map(x => Array(n).fill(0)); | ||
|
||
let i = 0; | ||
let j = (n - 1) / 2; | ||
|
||
magicSquare[i][j] = 1; | ||
|
||
for(let num = 2; num <= n * n; num++) | ||
{ | ||
let row = (i - 1 < 0) ? (n - 1) : (i - 1); | ||
let col = (j - 1 < 0) ? (n - 1) : (j - 1); | ||
|
||
if(magicSquare[row][col] === 0) | ||
{ | ||
i = row; | ||
j = col; | ||
} else { | ||
i = (i + 1) % n; | ||
} | ||
magicSquare[i][j] = num; | ||
} | ||
return magicSquare; | ||
} | ||
|
||
/* | ||
https://en.wikipedia.org/wiki/Magic_square#A_method_of_constructing_a_magic_square_of_doubly_even_order | ||
the detail of how to generate a magic matrix of doubly even order(4M) | ||
*/ | ||
function generate_square_doubly_even(n) | ||
{ | ||
let magicSquare = []; | ||
let num = 1; | ||
let diametrical_number = n * n + 1; | ||
for(let i = 0; i < n; i++) | ||
{ | ||
let temp = []; | ||
for(let j = 0; j < n; j++) | ||
{ | ||
temp.push(num); | ||
num++; | ||
} | ||
magicSquare.push(temp); | ||
} | ||
|
||
// for each 4x4 block, change the value that is not in the diagonal | ||
for(let i = 0; i < n; i += 4) | ||
{ | ||
for(let j = 0; j < n; j += 4) | ||
{ | ||
magicSquare[i][j + 1] = diametrical_number - magicSquare[i][j + 1]; | ||
magicSquare[i + 3][j + 2] = diametrical_number - magicSquare[i + 3][j + 2]; | ||
|
||
magicSquare[i][j + 2] = diametrical_number - magicSquare[i][j + 2]; | ||
magicSquare[i + 3][j + 1] = diametrical_number - magicSquare[i + 3][j + 1]; | ||
|
||
magicSquare[i + 1][j] = diametrical_number - magicSquare[i + 1][j]; | ||
magicSquare[i + 2][j + 3] = diametrical_number - magicSquare[i + 2][j + 3]; | ||
|
||
magicSquare[i + 2][j] = diametrical_number - magicSquare[i + 2][j]; | ||
magicSquare[i + 1][j + 3] = diametrical_number - magicSquare[i + 1][j + 3]; | ||
} | ||
} | ||
|
||
return magicSquare; | ||
} | ||
|
||
/* | ||
https://en.wikipedia.org/wiki/Conway%27s_LUX_method_for_magic_squares | ||
the detail of LUX method to generate 4M + 2 magic matrix | ||
*/ | ||
function generate_square_even(n) | ||
{ | ||
// initialize matrix with size n and set all values to 0 | ||
let magicSquare = []; | ||
for(let i = 0; i < n; i++) | ||
{ | ||
let temp = []; | ||
for(let j = 0; j < n; j++) | ||
{ | ||
temp.push(0); | ||
} | ||
magicSquare.push(temp); | ||
} | ||
|
||
// use LUX method to calculate magic matrix | ||
let M = (n - 2) / 4; | ||
let L = M; | ||
let U = M + 1; | ||
let X = M + 2; | ||
|
||
let odd_two_M = generate_square_odd(2*M+1); | ||
|
||
// compute the L row | ||
for(let i = 0; i <= L; i++) | ||
{ | ||
for(let j = 0; j < 2 * M + 1 ; j++) | ||
{ | ||
magicSquare[2*i][2*j] = 4 * odd_two_M[i][j]; | ||
magicSquare[2*i][2*j+1] = 4 * odd_two_M[i][j] - 3; | ||
magicSquare[2*i+1][2*j] = 4 * odd_two_M[i][j] - 2; | ||
magicSquare[2*i+1][2*j+1] = 4 * odd_two_M[i][j] - 1; | ||
} | ||
} | ||
|
||
// compute the U row | ||
for(let j = 0; j < 2 * M + 1; j++) | ||
{ | ||
magicSquare[2*U][2*j] = 4 * odd_two_M[U][j] - 3; | ||
magicSquare[2*U][2*j+1] = 4 * odd_two_M[U][j]; | ||
magicSquare[2*U+1][2*j] = 4 * odd_two_M[U][j] - 2; | ||
magicSquare[2*U+1][2*j+1] = 4 * odd_two_M[U][j] - 1; | ||
} | ||
|
||
// compute the X row | ||
for(let i = X; i < 2 * M + 1; i++) | ||
{ | ||
for(let j = 0; j < 2 * M + 1; j++) | ||
{ | ||
magicSquare[2*i][2*j] = 4 * odd_two_M[i][j] - 3; | ||
magicSquare[2*i][2*j+1] = 4 * odd_two_M[i][j]; | ||
magicSquare[2*i+1][2*j] = 4 * odd_two_M[i][j] - 1; | ||
magicSquare[2*i+1][2*j+1] = 4 * odd_two_M[i][j] - 2; | ||
} | ||
} | ||
|
||
// handle the special case | ||
magicSquare[2*L][2*M] = 4 * odd_two_M[L][M] - 3; | ||
magicSquare[2*L][2*M+1] = 4 * odd_two_M[L][M]; | ||
magicSquare[2*L+1][2*M] = 4 * odd_two_M[L][M] - 2; | ||
magicSquare[2*L+1][2*M+1] = 4 * odd_two_M[L][M] - 1; | ||
|
||
magicSquare[2*U][2*M] = 4 * odd_two_M[U][M]; | ||
magicSquare[2*U][2*M+1] = 4 * odd_two_M[U][M] - 3; | ||
magicSquare[2*U+1][2*M] = 4 * odd_two_M[U][M] - 2; | ||
magicSquare[2*U+1][2*M+1] = 4 * odd_two_M[U][M] - 1; | ||
|
||
return magicSquare; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
|
||
function magic_test() | ||
{ | ||
*import math:magic | ||
|
||
function check_magic(input) | ||
{ | ||
let magic_matrix = magic(input).clone().val; | ||
|
||
let sumd1 = 0; | ||
let sumd2 = 0; | ||
for (let i = 0; i < input; i++) | ||
{ | ||
// (i, i) is the diagonal from top-left -> bottom-right | ||
// (i, input - i - 1) is the diagonal from top-right -> bottom-left | ||
sumd1 = sumd1 + magic_matrix[i][i]; | ||
sumd2 = sumd2 + magic_matrix[i][input-1-i]; | ||
} | ||
|
||
// // if the two diagonal sums are unequal then it is not a magic square | ||
if(sumd1 !== sumd2) | ||
return false; | ||
|
||
// check if each row or column equal | ||
for (let i = 0; i < input; i++) { | ||
let colSum = 0; | ||
let rowSum = 0; | ||
for (let j = 0; j < input; j++) | ||
{ | ||
rowSum += magic_matrix[i][j]; | ||
colSum += magic_matrix[j][i]; | ||
} | ||
|
||
if (rowSum != colSum || colSum != sumd1) | ||
return false; | ||
} | ||
// print(input); | ||
// print("magic passed."); | ||
return true; | ||
} | ||
|
||
for(let i = 3; i <= 20; i++) | ||
{ | ||
if(!check_magic(i)) | ||
{ | ||
throw new Error("Failed unit test for magic function."); | ||
} | ||
} | ||
// print("all test passed."); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters