Javaを使って4行4列の16マスの魔方陣(1~16の数字を1つずつ使ったもの)をランダムで生成するコードを作成してみました。
魔方陣とは、以下のような縦・横・斜めの全ての列の合計が同じになる数表のことをいいます。今回は1~16の数字を一つずつ使ったものを作っていきます。
下に魔方陣の一例を示します。
$$ \large\begin{array}{|c|c|c|c|} \hline 1 & 14 & 15 & 4 \\ \hline 8 & 11 & 10 & 5 \\ \hline 12 & 7 & 6 & 9 \\ \hline 13 & 2 & 3 & 16 \\ \hline \end{array} $$
画像の魔方陣では、縦・横・斜めのどの列も合計が34になっていることが分かります。この性質を利用して、段階を踏みながらこのような表を出力してみます。
「4次魔方陣を求めるプログラム | 大同大学」にあるアルゴリズムを参考にして、Javaで実装してみます。
1import java.util.ArrayList;
2import java.util.Collections;
3import java.util.List;
4
5public class MakeMagicSquare44 {
6
7 private static final int MAGIC_SUM = 34;
8 private static final int SIZE = 16;
9 private static final int DIM = 4;
10
11 /** 1から16までを格納するArrayList */
12 private List<Integer> list = new ArrayList<>();
13
14 /** 完成した魔方陣の数表を格納する配列 */
15 private int[] array = new int[SIZE];
16
17 public static void main(String[] args) {
18 MakeMagicSquare44 magicSquare = new MakeMagicSquare44();
19 magicSquare.execute();
20 magicSquare.showArray();
21 }
22
23 /** 魔方陣の作成 */
24 private void execute() {
25 while (true) {
26 generateList();
27 initializeCorners();
28 if (attemptFillMagicSquare()) {
29 break;
30 }
31 }
32 }
33
34 /** listに1~16を格納しシャッフルする */
35 private void generateList() {
36 list.clear();
37 for (int i = 1; i <= SIZE; i++) {
38 list.add(i);
39 }
40 Collections.shuffle(list);
41 }
42
43 /** 初期のコーナー要素を設定 */
44 private void initializeCorners() {
45 array[0] = list.remove(0);
46 array[3] = list.remove(0);
47 }
48
49 /** 魔方陣を埋める試みを行う */
50 private boolean attemptFillMagicSquare() {
51 return checkCombinationAndFill(0, 3, 12, 15) &&
52 checkCombinationAndFill(0, 15, 5, 10) &&
53 checkCombinationAndFill(0, 3, 1, 2) &&
54 checkCombinationAndFill(3, 12, 6, 9) &&
55 fillLastOne(1, 5, 9, 13) &&
56 fillLastOne(2, 6, 10, 14) &&
57 checkCombinationAndFill(5, 6, 4, 7) &&
58 fillLastOne(0, 4, 12, 8) &&
59 fillLastOne(3, 7, 15, 11);
60 }
61
62 /** 2つの数字が埋まっている列に対して、残りの2つの数字をlistから決定する */
63 private boolean checkCombinationAndFill(int index1, int index2, int index3, int index4) {
64 for (int i = 0; i < list.size() - 1; i++) {
65 for (int j = i + 1; j < list.size(); j++) {
66 if (array[index1] + array[index2] + list.get(i) + list.get(j) == MAGIC_SUM) {
67 array[index3] = list.remove(i);
68 array[index4] = list.remove(j - 1);
69 return true;
70 }
71 }
72 }
73 return false;
74 }
75
76 /** 3つの数字が埋まっている列に対して、残りの1つの数字をlistから決定する */
77 private boolean fillLastOne(int index1, int index2, int index3, int index4) {
78 for (int i = 0; i < list.size(); i++) {
79 if (array[index1] + array[index2] + array[index3] + list.get(i) == MAGIC_SUM) {
80 array[index4] = list.remove(i);
81 return true;
82 }
83 }
84 return false;
85 }
86
87 /** 配列を数表形式で出力する */
88 private void showArray() {
89 for (int i = 0; i < SIZE; i++) {
90 System.out.printf("%3d", array[i]);
91 if ((i + 1) % DIM == 0) {
92 System.out.println();
93 }
94 }
95 }
96}
実行結果の一例が以下になります。
1 8 13 11 2
2 5 10 16 3
3 12 7 1 14
4 9 4 6 15
ランダム出力なので実行毎に出力結果が変わります。
魔方陣の性質を利用して順に数字を求めていくことで、比較的簡単に生成することができました。以上で記事を終わりにします。