マルチスレッド版数独自動生成ソフトC++コードを題材とする超初心者のためのVisual Studio C++講義
第6章 8次魔方陣・8次魔方陣の作成
⁂私が2022を使っている間に2026の設定が変わってしまっています。
がUTF‑8になっている場合にはShift-JISに変更して
エンコード付きで保存してください。⁂
第12話 8次魔方陣の作成と小さい順での並び替え

黄色を中心の点に対して点対称移動
明るい紫を中央の直線に対して線対称移動
緑を中央の直線に対して線対称移動
による8次魔方陣の作成

(
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
260
64 2 59 5 4 62 7 57 260
16 55 11 52 53 14 50 9 260
41 23 46 20 21 43 18 48 260
25 34 30 37 36 27 39 32 260
33 26 38 29 28 35 31 40 260
17 47 22 44 45 19 42 24 260
56 15 51 12 13 54 10 49 260
8 58 3 61 60 6 63 1 260
260 260 260 260 260 260 260 260 260
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];
}
//8次魔方陣 + 各合計の表示
cout << endl;
for (int i = 0; i < n; i++) {
//ホームページビルダーでは半角スペースを4つ
//入れることができないできず、しかも
//全角スペースを入れると文字化けしますので変則的な書き方になっています。
cout << " ";
cout << " ";
cout << " ";
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] << " "; //8次魔方陣
+ 各合計の表示終了
//以降魔方陣を小さい順に並べ替える作業
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;
}
実は前話では(4 * n + 2)次魔方陣の革命が起きたおり、
今話では(4 * n)次魔方陣の革命が起きました。
どういう意味でしょうか。
ヒントは、
//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に保存
などの解釈文にあります。
いったい何の革命が起きたのでしょうか。
その革命は驚異的なものです。
次話で答えを聞いたら皆さんが驚愕すること間違いなしです。
今まで私は偶数次魔方陣は
8,12,16,・・・
と
6,10,14,・・・
の2系列なると思っていましたが、
この二つは統一できます。
しかも、4も含めることができます。
2次魔方陣が存在しないことは、
中学生レベルの数学で簡単に証明できますから、
存在するすべて偶数次を統一する大統一理論(大は4次も含むという意味です)が可能だということです。