マルチスレッド版数独自動生成ソフトC++コードを題材とする超初心者のためのVisual Studio C++講義
第6章 6次魔方陣・8次魔方陣の作成
第13話 8以上の4の倍数次魔方陣の考え方
第11話と第12話で革命が起きたと書きましたが、
第28回 新魔方陣の法則(偶数次版魔方陣の法則)第1弾
第29回 新魔方陣の法則(偶数次版魔方陣の法則)第2弾
を読み返すと、判断が少し早すぎました。
第13話では8以上の4の場合数次魔方陣の場合について見ていきます。
つまり、8,12,16,・・・という系列の魔方陣の場合について見ていくということです。
理由はこちらの場合の方が、単純であるからです。
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
| 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
| 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
| 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
| 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
| 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 |
(同じもの図にしました)
この図を見て
#include<iostream>//インクルードファイルiostreamの読み込み
#include<conio.h>//while(!_kbhit());を使うためのお呪い
#include<string> //文字列変数を使えるようにするために組み込む
#include <iomanip> //setprecisionを使えるように組み込む
#include <cmath>//powなどを使うときに必要
#include <ctime>//time()(←現時刻発生する関数)を使うために必要
using namespace std;//coutを使うときに必要なお呪い
void 魔方陣();//横と縦の2方向を持つ2次元for文体験
const int n = 8;//これからは具体的な数字を使わずにnとする。左の8を12などに変更すれば12次魔方陣に対応する
//n = 6 の一か所だけ具体的な数字を使い、他ではすべて n を使ってください。
int main() {//私は社長だ。
魔方陣();
while (!_kbhit());//待機させるための命令
return(0);//int main()終わるためのお呪い
}
void 魔方陣() {
int a[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
a[i][j] = n * i + j + 1;//自然配列を生成してaに収納する
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) { //自然配列に体裁を整えて表示する。
if (a[i][j] < 10)cout << " ";
cout << a[i][j] << " ";
}
cout << endl;
}
cout << endl; int u;//a[i][j]などのデータを一時保存する
//対角線部分を交換する
for (int i = 0; i < n / 2; i++) {//2026年2月17日修正 修正内容(3 → n / 2) 修正理由具体的数字3が入ってしまっている
u = a[i][i];
//a[i][i]のデータがa[n - 1 - i][n - 1 - i]のデータに上書きされる前にuに保存
a[i][i] = a[n - 1 - i][n - 1 - i]; //a[i][i]のデータをa[n - 1 - i][n -
1 - i]のデータで上書き
a[n - 1 - i][n - 1 - i] = u; //a[n - 1 - i][n - 1 - i]のデータをa[i][i]の元データで保存
}
//対角線部分の交換終了
//逆対角線部分を交換する
for (int i = 0; i < n / 2; i++) {//2026年2月17日修正 修正内容(3 → n / 2) 修正理由
具体的数字3が入ってしまっている
u = a[i][n - 1 - i]; //a[i][n - 1 - i]のデータがa[n - 1 - i][i]のデータに上書きされる前にuに保存
a[i][n - 1 - i] = a[n - 1 - i][i]; //a[i][n - 1 - i]のデータをa[n - 1 -
i][i]のデータで上書き
a[n - 1 - i][i] = u; //a[n - 1 - i][i]のデータをa[i][n - 1 - i]の元データで保存
}
//逆対角線部分の交換終了 //明るい紫色の部分の交換
for (int i = 0; i < n / 2; i++) {//2026年2月16日修正 修正内容(3→ n / 2) 修正理由
具体的数字3が入ってしまっている
//1行目から4行目の左右対称移動
u = a[i][(3 + i) % (n / 2)];//2026年2月16日修正 修正内容(3→ (n / 2)) 修正理由 具体的数字3が入っていまっている
//a[i][(3 + i) % (n / 2)]のデータがa[i][n - 1 - ((3 + i) % (n / 2))]
// のデータ上書きされる前にuに保存
a[i][(3 + i) % (n / 2)] = a[i][n - 1 - ((3 + i) % (n / 2))];
//2026年2月16日修正 修正内容(3→ (n / 2)) 修正理由 具体的数字3が入っていまっていた
//a[i][(3 + i) % (n / 2)]のデータをa[i][n - 1 - ((3 + i) % (n / 2))]のデータで上書き
a[i][n - 1 - ((3 + i) % (n / 2))] = u;
//2026年2月16日修正 修正内容(3→ (n / 2)) 修正理由 具体的数字3が入っていまっていた
//a[i][n - 1 - ((3 + i) % (n / 2))]のデータをa[i][(3 + i) % (n / 2)]の元データで上書き
//1行目から4行目の左右対称移動終了
//5行目から8行目までの左右対称移動
u = a[n - 1 - i][(3 + i) % (n / 2)];
//2026年2月16日修正 修正内容(3→ (n / 2)) 修正理由 具体的数字3が入っていまっていた
//a[n - 1 - i][(3 + i) % (n / 2)]のデータがa[n - 1 - i][n - 1 - ((3 + i) % (n / 2))]
// のデータ上書きされる前にuに保存
a[n - 1 - i][(3 + i) % (n / 2)] = a[n - 1 - i][n - 1 - ((3 + i) % (n
/ 2))];
//2026年2月16日修正 修正内容(3→ (n / 2)) 修正理由 具体的数字3が入っていまっていた
//a[n - 1 - i][(3 + i) % (n / 2)]のデータをa[n - 1 - i][n - 1 - ((3 + i) % (n / 2))]のデータで上書き
a[n - 1 - i][n - 1 - ((3 + i) % (n / 2))] = u;
//2026年2月16日修正 修正内容(3→ (n / 2)) 修正理由 具体的数字3が入っていまっていた
//a[n - 1 - i][n - 1 - ((3 + i) % (n / 2))]のデータをa[n - 1 - i][(3 + i)
% (n / 2)]の元データで上書き
//5行目から8行目までの左右対称移動終了
}
//明るい紫色の部分の交換終了
//緑の部分の交換
for (int i = 0; i < n / 2; i++) {
//2026年2月16日修正 修正内容(3→ n / 2) 修正理由 具体的数字3が入ってしまっていた
//1列目から4列までの上下対称移動
u = a[i][(2 + i) % (n / 2)];
//2026年2月17日修正 修正内容(% 3→ % (n / 2)) 修正理由 具体的数字3が入っていまっていた
// a[i][(2 + i) % (n / 2)]のデータがa[n - 1 - i][(2 + i) % (n / 2)]
// のデータに上書きされる前にuに保存
a[i][(2 + i) % (n / 2)] = a[n - 1 - i][(2 + i) % (n / 2)];
//2026年2月17日修正 修正内容(% 3→ % (n / 2)) 修正理由 具体的数字3が入っていまっている
//a[i][(2 + i) % (n / 2)]のデータをa[n - 1 - i][(2 + i) % (n / 2)]のデータで上書き
a[n - 1 - i][(2 + i) % (n / 2)] = u;
//2026年2月17日修正 修正内容(% 3→ % (n / 2)) 修正理由 具体的数字3が入っていまっている
//a[n - 1 - i][(2 + i) % (n / 2)]のデータをa[i][(2 + i) % (n / 2)]の元データで上書き
//1列目から4列までの上下対称移動終了
//5列目から8列までの上下対称移動
u = a[i][n - 1 - (2 + i) % (n / 2)];
//2026年2月17日修正 修正内容(% 3→ % (n / 2)) 修正理由 具体的数字3が入っていまっていた
//a[i][(n - 1 - (2 + i) % (n / 2)]のデータがa[n - 1 - i][n - 1 - (2 + i)
% (n / 2)] // のデータに上書きされる前にuに保存
a[i][n - 1 - (2 + i) % (n / 2)] = a[n - 1 - i][n - 1 - (2 + i) % (n
/ 2)];
//2026年2月17日修正 修正内容(% 3→ % (n / 2)) 修正理由 具体的数字3が入っていまっていた
//a[i][n - 1 - (2 + i) % (n / 2)]のデータをa[n - 1 - i][n - 1 - (2 + i) % (n / 2)]のデータで上書き
a[n - 1 - i][n - 1 - (2 + i) % (n / 2)] = u;
//2026年2月17日修正 修正内容(% 3→ % (n / 2)) 修正理由 具体的数字3が入っていまっていた
//a[n - 1 - i][n - 1 - (2 + i) % (n / 2)]のデータをa[i][n - 1 - (2 + i) %
(n / 2)]の元データで上書き
//5列目から8列までの上下対称移動終了
}
//緑の部分の交換終了
int g[n];//g(行合計計算配列)の定義
for (int i = 0; i < n; i++)g[i] = 0;//すべて0に初期化
int r[n];//r(列合計計算配列)の定義
for (int i = 0; i < n; i++)r[i] = 0;//すべて0に初期化
int t[2] = { 0,0 };//t(対角線合計配列)の定義と初期化
//各行の合計計算
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
g[i] = g[i] + a[i][j];
}
}
//各列の合計計算
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
r[i] = r[i] + a[i][j];
}
}
//各対角線の合計計算
for (int i = 0; i < n; i++) {
t[0] = t[0] + a[i][i];
t[1] = t[1] + a[i][n - 1 - i];
}
//6次魔方陣 + 各合計の表示
cout << endl;
for (int i = 0; i < n; i++) {
cout << " ";
}
cout << " " << t[1] << endl;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (a[i][j] < 10)cout << " ";
cout << " " << a[i][j] << " ";
}
cout << " " << g[i];
cout << endl;
}
cout << endl;
for (int i = 0; i < n; i++) {
cout << r[i] << " ";
}
cout << " " << t[0] << " "; //6次魔方陣 + 各合計の表示終了
//以降魔方陣を小さい順に並べ替える作業
int c[n * n];//魔方陣を複写するための1次元配列
//魔方陣の内容を複写のための1次元配列にコピー
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
c[n * i + j] = a[i][j];
}
}
//c[i][j]の並び替え
for (int i = 0; i < n * n; i++) {
int min = 100;//最小値を表すminと定義して、最小ととしてあり得ない100で初期化
int jk;//最小値になる j を保存するための変数
for (int j = i; j < n * n; j++) {
if (c[j] < min) {
min = c[j];//最小値の更新
jk = j;//その際jの記録
}
}
//c[i]のデータとc[k]のデータを交換する
if (jk > i) {
u = c[i];//上書きされる前にc[i]のデータを保存
c[i] = c[jk];//c[i]のデータをc[k]のデータで上書き
c[jk] = u;//c[k]のデータをc[i]の元データで上書き
}
//c[i]のデータとc[k]のデータを交換する終了
}
//以降魔方陣を小さい順に並べ替える作業終了
//並び変えたデータを体裁を整えて表示
cout << endl << endl;
for (int i = 0; i < n * n; i++) {
if (c[i] < 10)cout << " ";
//c[i]のデータが一桁の場合半角スペースを入れる
cout << c[i] << " ";
if (i % n == n - 1)cout << endl;
}
cout << endl;
}
はすでに普遍版を実現していると考えました。
基本具体的な数字でコーティングしておらず、
すべて n で表しているので、
const int n = 8;
の部分を
const int n = 12;
const int n = 16;
・
・
・
と変更していけば12次魔方陣、16次魔方陣、20次魔方陣、・・・、4 * m(mは整数) 次魔方陣、・・・
4の倍数次魔方陣に普遍版ができた早とちりしたわけですが、
第28回 新魔方陣の法則(偶数次版魔方陣の法則)第1弾を読んでいただければわかる通り、
| 1 | 2 | 3 | 4 |
| 9 | 10 | 11 | 12 |
| 17 | 18 | 19 | 20 |
| 25 | 26 | 27 | 28 |
上で左の1/4に注目して色を塗り、
後は下の線と右の線に鏡を置いて、
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
| 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
| 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
| 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
| 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
| 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 |
(同じもの図にしました)
ただし、鏡によって映し取られるものは色だけです。
ですから、
const int n = 12;
とすれば12次魔方陣
const int n = 16
とすれば16次魔方陣
ができて4倍数字の場合の普遍版ができると判断しましたが、
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
| 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
| 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
| 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
| 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
| 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 |
実は上左1/4
| 1 | 2 | 3 | 4 |
| 9 | 10 | 11 | 12 |
| 17 | 18 | 19 | 20 |
| 25 | 26 | 27 | 28 |
のブロックに注目して、右下へ下がっていく斜め行に注目したときに、
白1色、黄色1色、緑1色、明るい紫1色と解釈してしまいましたが、
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |
| 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
| 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |
| 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 |
| 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 |
| 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 |
| 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 |
| 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 |
| 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 |
| 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 |
| 1 | 2 | 3 | 4 | 5 | 6 |
| 13 | 14 | 15 | 16 | 17 | 18 |
| 25 | 26 | 27 | 28 | 29 | 30 |
| 37 | 38 | 39 | 40 | 41 | 42 |
| 49 | 50 | 51 | 52 | 53 | 54 |
| 61 | 62 | 63 | 64 | 65 | 66 |
だと白だけ3行になってしまいます。
第1行目だけに注目してください。
黄色は中心に対して点対称移動
緑は中心の直線に対して左右線対称移動
明るい紫は中心の直線に対して上下線対称移動でした。
すると1行目と最終行の色は
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |
| 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
| 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |
| 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 |
| 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 |
| 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 |
| 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 |
| 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 |
| 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 |
| 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 |
(本当は全部に色塗りたいのですが、ひとますずつ塗らなければならず大変肩の凝る作業なので、
お許し下さい。)
ということにあります。
黄色は中心の点に対して点対称移動
緑は中心の直線に対して上下線対称移動
明るい紫は中央の直線に対して線対称移動ですから、
第一行は
144,2,135,9,5,6,7,9,41,142,133
となります。足し算を実行してください。
144+2+135+9+5+6+7+9+41+142+133 = 633
となりますが、12次魔方陣の場合各行の和は
(1+2+3+・・・+144) ÷ 12 = 10440 ÷12 = 870
ですから一致しません。
色塗りは黄色→白→緑→明るい紫の順に塗るのですが、
黄色と白は1色ずつです。
それだと足りません。
正しくは、全体を見る場合には
黄色2色、白2色、緑4色、明るい紫4色
であり、
上左1/4を見るときには
黄色1色、白1色、緑2色、明るい紫2色
なのです。ですから、
黄色→白→緑→明るい紫→緑→明るい紫
なのです(正確には、黄色は先頭に来なければなりませんが、
白は1色、緑2色、明るい紫2色の原則が守られれば順番は自由です)。
すると、
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |
| 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
| 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |
| 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 |
| 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 |
| 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 |
| 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 |
| 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 |
| 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 |
| 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 |
すると第1行は
144,2,135,9,137,7,6,140,4,142,11,133から
144+2+135+9+137+7+6+140+4+142+11+133 = 870
で一致します。
念のために最終行も見ておくと
12,134,3,141,5,139,138,8,136,10,143,1から
12+124+3+141+5+139+138+8+136+10+143+1 = 870
でご名答です。
| 1 | 2 | 3 | 4 | 5 | 6 |
| 13 | 14 | 15 | 16 | 17 | 18 |
| 25 | 26 | 27 | 28 | 29 | 30 |
| 37 | 38 | 39 | 40 | 41 | 42 |
| 49 | 50 | 51 | 52 | 53 | 54 |
| 61 | 62 | 63 | 64 | 65 | 66 |
は正しくは
| 1 | 2 | 3 | 4 | 5 | 6 |
| 13 | 14 | 15 | 16 | 17 | 18 |
| 25 | 26 | 27 | 28 | 29 | 30 |
| 37 | 38 | 39 | 40 | 41 | 42 |
| 49 | 50 | 51 | 52 | 53 | 54 |
| 61 | 62 | 63 | 64 | 65 | 66 |
なのです。
16魔方陣なら 上左1/4を見るときには
黄色1色、白1色、緑3色、明るい紫3色
です。
20魔方陣なら 上左1/4を見るときには
黄色1色、白1色、緑4色、明るい紫4色
です。
明確な法則性があります。
黄色1色、白1色
は固定です。
すると12次場合は
(12 / 2 - 2) ÷ 2 = 2
の計算から緑と明るい紫はそれぞれ2色となります。
16次場合は
(16 / 2 - 2) ÷ 2 = 3
の計算から緑と明るい紫はそれぞれ3色となります。
20次の場合は
(20 / 2 - 2) ÷2 = 4
の計算から緑と明るい紫はそれぞれ4色となります。
では、コードを変更して8以上の4の倍数次魔方陣のコードを作り、
8以上の4の倍数次魔方陣普遍版を完成させてください。
第14話では今回の主張が正しいことを数学的に証明して、
第15話で答え合わせをしていきます。