マルチスレッド版数独自動生成ソフトC++コードを題材とする超初心者のためのVisual Studio C++講義
第13章 様々な魔方陣の作成および自動生成
第9話 9次魔方陣

(
種1
4 0 6 7 1 3 2 8 5
5 4 0 6 7 1 3 2 8
8 5 4 0 6 7 1 3 2
2 8 5 4 0 6 7 1 3
3 2 8 5 4 0 6 7 1
1 3 2 8 5 4 0 6 7
7 1 3 2 8 5 4 0 6
6 7 1 3 2 8 5 4 0
0 6 7 1 3 2 8 5 4
種2
1 0 7 3 8 2 5 6 4
0 7 3 8 2 5 6 4 1
7 3 8 2 5 6 4 1 0
3 8 2 5 6 4 1 0 7
8 2 5 6 4 1 0 7 3
2 5 6 4 1 0 7 3 8
5 6 4 1 0 7 3 8 2
6 4 1 0 7 3 8 2 5
4 1 0 7 3 8 2 5 6
魔方陣表示
38 1 62 67 18 30 24 79 50
46 44 4 63 66 15 34 23 74
80 49 45 3 60 70 14 29 19
22 81 48 42 7 59 65 10 35
36 21 78 52 41 2 55 71 13
12 33 25 77 47 37 8 58 72
69 16 32 20 73 53 40 9 57
61 68 11 28 26 76 54 39 6
5 56 64 17 31 27 75 51 43
魔方陣です。
プロジェクト終了
)
を実現するコード例
#include<iostream>//インクルードファイルiostreamの読み込み
#include<conio.h>//while(!_kbhit());を使うためのお呪い
#include<string> //文字列変数を使えるようにするために組み込む
#include <iomanip> //setprecisionを使えるように組み込む
#include <cmath>//powなどを使うときに必要
#include <ctime>//time()(←現時刻発生する関数)を使うために必要
using namespace std;//coutを使うときに必要なお呪い
void 魔方陣作成関数();
const size_t n = 9;
size_t mg = n * (n * n + 1) / 2;
size_t シード値 = (unsigned)time(NULL);
int main() {//私は社長だ。
srand(シード値);
魔方陣作成関数();
cout << endl;
cout << "プロジェクト終了" << endl;
while (!_kbhit());//待機させるための命令
return 0;//int main() を終わるためのお呪い
}
void 魔方陣作成関数() {
size_t t1[n][n], t2[n][n];//魔方陣の種を収納する2次元配列
size_t m[n][n];//5次魔方陣を収納する2次元配列
cout << "種1" << endl;
//以下第1行の作成 先頭を真ん中の数字にして以降は重複なしにn未満の整数をランダムに入れる
t1[0][0] = n / 2;
for (size_t i = 1; i < n; i++) {
while (1) {
size_t h = 1;
t1[0][i] = rand() % n;
for (size_t j = 0; j < i; j++) {
if (t1[0][j] == t1[0][i]) {
h = 0;
break;
}
}
if (h == 1)break;
}
}
for (size_t i = 1; i < n; i++) {//2行目以降の1ずらしを実行
for (size_t j = 0; j < n; j++) {
t1[i][j] = t1[0][(j + 8 * i) % n];
}
}
for (size_t i = 0; i < n; i++) {
for (size_t j = 0; j < n; j++) {
cout << " " << t1[i][j] << " ";
}
cout << endl;
}
cout << endl;
cout << "種2" << endl;
//以下第1行の作成 最後を真ん中の数字にして以降は重複なしにn未満の整数をランダムに入れる
size_t e[n];
for (size_t i = 0; i < n; i++)e[i] = 0;
t2[0][n - 1] = n / 2;
e[t2[0][n - 1]] = 1;
for (int i = n - 2; i >= 0; i--) {
while (true) {
size_t r = rand() % n;
if (e[r] == 0) {
t2[0][i] = r;
e[r] = 1;
break;
}
}
}
for (size_t i = 1; i < n; i++) {//2行目以降の逆1ずらしを実行
for (size_t j = 0; j < n; j++) {
t2[i][j] = t2[0][(j + i) % n];
}
}
for (size_t i = 0; i < n; i++) {
for (size_t j = 0; j < n; j++) {
cout << " " << t2[i][j] << " ";
}
cout << endl;
}
cout << endl;
//奇数次魔方陣作成
for (size_t i = 0; i < n; i++) {
for (size_t j = 0; j < n; j++) {
m[i][j] = n * t1[i][j] + t2[i][j] + 1;
}
}
//奇数次魔方陣表示
cout << endl;
cout << "魔方陣表示" << endl;
for (size_t i = 0; i < n; i++) {
for (size_t j = 0; j < n; j++) {
if (m[i][j] < 10) {
cout << " " << m[i][j] << " ";
}
else {
cout << m[i][j] << " ";
}
}
cout << endl;
}
//魔方陣になっているか検証
size_t c = 1;
size_t gk = 0;
for (size_t i = 0; i < n; i++)gk += m[i][i];
if (gk != mg)c = 0;
if (c == 1) {
gk = 0;
for (size_t i = 0; i < n; i++)gk += m[i][n - 1 - i];
if (gk != mg) {
c = 0;
cout << gk << endl;
}
}
if (c == 1) {
for (size_t i = 0; i < n; i++) {
gk = 0;
for (size_t j = 0; j < n; j++) {
gk += m[i][j];
}
if (gk != mg) {
c = 0;
cout << gk << endl;
break;
}
}
}
if (c == 1) {
for (size_t i = 0; i < n; i++) {
gk = 0;
for (size_t j = 0; j < n; j++) {
gk += m[j][i];
}
if (gk != mg) {
c = 0;
cout << gk << endl;
break;
}
}
}
if (c == 1)cout << "魔方陣です。" << endl; else cout << "魔方陣ではありません。" << endl;
}
今回も具体的な数字を使わずにnで書いていますから、
const size_t n = 9;をconst size_t n = 15;
とすれば15次魔方陣になります。
ただし、形が崩れるので整形は必要ですが。
さらに、いうとconst size_t n = 7; でも大丈夫です。
7も当然奇数ですから、大丈夫なわけです。
ただし、完全魔方陣には当然なりません。
具体的な数字を使わずにコーティングすると普遍版になるのです。
ですから、なるべく具体的な数字ではなくn等を使って書くようにすることを習慣にしましょう。
マルチスレッドプログラミングによって4次から8次までの魔方陣の1個生成に挑戦します。
それに成功した後に
構造解析型(大げさな名前ですが、数独自動生成を開発しているときに思いついた全体構造解析を転用しています)、
商と余り分解する方法をその次に紹介します。
2段階の改良によって6次魔方陣を1個当たりの平均で0.06秒台での生成を実現します。
第13章第8話へ 第13章10話へ
本講義トップへ