マルチスレッド版数独自動生成ソフトC++コードを題材とする超初心者のためのVisual Studio C++講義
第7章 関数の学習
第9話 新しい職制による8次魔方陣作成
実行画面

(
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
64 2 3 4 5 6 7 57
9 55 11 12 13 14 50 16
17 18 46 20 21 43 23 24
25 26 27 37 36 30 31 32
33 34 35 29 28 38 39 40
41 42 22 44 45 19 47 48
49 15 51 52 53 54 10 56
8 58 59 60 61 62 63 1
64 2 59 4 5 62 7 57
9 55 11 52 53 14 50 16
41 18 46 20 21 43 23 48
25 34 27 37 36 30 39 32
33 26 35 29 28 38 31 40
17 42 22 44 45 19 47 24
49 15 51 12 13 54 10 56
8 58 3 60 61 6 63 1
64 2 59 5 4 62 7 57
16 55 11 52 53 14 50 9
41 23 46 20 21 43 18 48
25 34 30 37 36 27 39 32
33 26 38 29 28 35 31 40
17 47 22 44 45 19 42 24
56 15 51 12 13 54 10 49
8 58 3 61 60 6 63 1
)
コード例
#include<iostream>//インクルードファイルiostreamの読み込み
#include<conio.h>//while(!_kbhit());を使うためのお呪い
#include<string> //文字列変数を使えるようにするために組み込む
#include <iomanip> //setprecisionを使えるように組み込む
#include <cmath>//powなどを使うときに必要
#include <ctime>//time()(←現時刻発生する関数)を使うために必要
using namespace std;//coutを使うときに必要なお呪い
const int n = 8;//具体的な数字を使うのではなく、 n を使うと汎用性のあるプログラムになる!
//部長の任務は社長の仕事の一部を引き受け、
//社員(部の社員)に仕事を命令することです。
void 部長(int a[n][n]);
void 課長1(int a[n][n]);//自然配列担当社員と表示担当社員の統率を担当
void 課長2(int a[n][n]);//対角線部分交換社員fと表示担当職員の統率を担当
void 課長3(int a[n][n]);//緑部分交換社員gと表示担当職員の統率を担当
void 課長4(int a[n][n]);//明るい紫部分交換社員fと表示担当職員の統率を担当
void 自然配列担当社員(int a[n][n]);
void 表示担当社員(int a[n][n]);
//fの任務は各対角線部分を点対称移動させることである
void f(int a[n][n]);
//gの任務は各対角線部分を点対称移動させることである
void g(int a[n][n]);
//hの任務は各対角線部分を点対称移動させることである
void h(int a[n][n]);
int main() {//私は社長だ。
int a[n][n];//2次元配列を定義(用意)した。
部長(a);//部長に統率を依頼
while (!_kbhit());//待機させるための命令
return 0;//int main() を終わるためのお呪い
}
void 部長(int a[n][n]) {
課長1(a);//自然配列担当社員と表示担当社員の統率を担当
課長2(a);//対角線部分交換社員fと表示担当職員の統率を担当
課長3(a);//緑部分交換社員gと表示担当職員の統率を担当
課長4(a);//明るい紫部分交換社員fと表示担当職員の統率を担当
}
void 課長1(int a[n][n]) {
自然配列担当社員(a);
表示担当社員(a);
}
void 課長2(int a[n][n]) {
f(a);//対角線部分交換
cout << endl;
表示担当社員(a);
}
void 課長3(int a[n][n]) {
g(a);//緑部分交換
cout << endl;
表示担当社員(a);
}
void 課長4(int a[n][n]) {
h(a);//明るい紫部分交換
cout << endl;
表示担当社員(a);
}
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;//自然配列を2次元配列に収納
}
}
}
void 表示担当社員(int a[n][n]) {
//自然配列を体裁を整えてコンソール画面に表示する
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;
}
}
//fの任務は対角線部分を点対称移動させることである
void f(int a[n][n]) {
//対角線点対称移動
for (int i = 0; i < n / 2; i++) {
int u;//交換のための受け皿
//対角線部分を点対称移動する作業を始める
u = a[i][i];
//a[i][i]のデータがa[n - i][n - i]のデータに上書きされる前に
//uにa[i][i]のデータを保存
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[i][i]の元データでa[n - 1 - i][n - 1 - i]のデータを上書き
//対角線部分を点対称移動する作業を終了
//逆対角線部分を点対称移動する作業を始める
u = a[i][n - 1 - i];
//a[i][n - 1 - i]のデータがa[n - 1 - i][i]のデータに上書きされる前に
//uにa[i][n - 1 - i]のデータを保存
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[i][i]の元データでa[n - 1 - i][n - 1 - i]のデータを上書き
//逆対角線部分を点対称移動する作業を終了
}
}
//gの任務は緑部分を線対称移動させることである
void g(int a[n][n]) {
for (int i = 0; i < n / 2; i++) {
//1列目から4列までの上下対称移動
int u;//交換のための受け皿
u = a[i][(2 + i) % (n / 2)];
// 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)];
//a[i][(2 + i) % (n / 2)]のデータをa[n - 1 - i][(2 + i) % (n / 2)]のデータで上書き
a[n - 1 - i][(2 + i) % (n / 2)] = u;
//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)];
//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)];
//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;
//a[n - 1 - i][n - 1 - (2 + i) % (n / 2)]のデータをa[i][n - 1 - (2 + i) % (n / 2)]の元データで上書き
//5列目から8列までの上下対称移動終了
}
//緑の部分の交換終了
}
//hの任務は明るい紫部分を線対称移動させることである
void h(int a[n][n]) {
//明るい紫色の部分の交換を始める
for (int i = 0; i < n / 2; i++) {
int u;//交換のための受け皿
//1行目から4行目の左右対称移動
u = a[i][(3 + i) % (n / 2)];
//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))];
//a[i][(3 + i) % (n / 2)]のデータをa[i][n - 1 - ((3 + i) % (n / 2))]のデータで上書き
a[i][n - 1 - ((3 + i) % (n / 2))] = u;
//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)];
//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))];
//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;
//a[n - 1 - i][n - 1 - ((3 + i) % (n / 2))]のデータをa[n - 1 - i][(3 + i) % (n / 2)]の元データで上書き
//5行目から8行目までの左右対称移動終了
}
//明るい紫色の部分の交換終了
}
8次魔方陣は2回目の登場ですが、
前回に比べたら大分すっきりしていませんか。
職制という構造を作ったのでわかりやすくなったのです。
関数の原則は1人でいろいろな仕事をしないことです。
分業して協力し合って進めるとプログラミングもわかりやすくなります。
第6章の最後の方で、
魔方陣の構造 = 数学の構造 = 会社組織の構造 = 世の中の仕組み
と書きましたが、数学の構造はあちらこちらにあって、
プログラミングも同じです。
ですから、さらに
魔方陣の構造 = 数学の構造 = 会社組織の構造 = 世の中の仕組み = プログラミングの構造
を加えましょう。
もし、お子さんやお孫さんが数学なんて勉強したって何の役にも立たないと、
言っている場合には、
いやそうではない、伝えてください。
生成AIの存在がライプニッツの夢だった普遍数学が実在していることを証明してます。
普遍数学とはいかなる問題も数学の方程式で解けるという主張です。
生成AIは小説を書き、詩を書き、論文を書き、動画も生成します。
生成AIは深層学習という手法を使って急激に賢くなって、
シンギュラリティ(学者の定義によって異なりますが、1台のコンピュータの知能が人類の総知能を超える)
が現実味を増しています。
来年に汎用性のあるAIができて、
超知能も2018年には出来ると予想するAIの専門家は多いです。
深層学習とは、人間の脳のシステムをまねたものです。
私たちは、まったく意識していませんが、
AIと同様に私たちは三角関数や微分積分を無意識のレベルで使って深層学習を成立させています。
ですから、お子さんやお孫さんが数学なんて絶対に社会に出てから使わないと、
主張したら私たちは毎日三角関数や微分積分を使っていること教えてやってください。
数学の学習、プログラミングの学習はとても大切なものであること教えてやってください。
そして、本サイトのプログラミング学習に取り組むとよいと勧めてください。
さて、構造化をさらに進展させます。
関数f・g・hは、一見異なる仕事をしているようですが、
交換という本質に注目するとき、
同じ仕事をしています。
そこで1次元配列を用意して
int x[2];//1次元配列の定義
x[0] = a[i][i];
x[1] = a[n - i - 1]a[n - i - 1]
などとして
void 交換係社員(int x[2]);
に
交換係社員(x)
1次元配列の先頭アドレスを伝えてx[0]とx[1]を交換してもらいます。
第7章第8話へ 第7章第10話へ
本講義トップへ