第31講 数独(ナンバープレイス)問題解決ソフトVer.2の制作
(数独(ナンバープレイス)問題作成ソフトに挑戦する人は☆☆)
第12話 第9話入力順の構築コード解説その1
コード再掲
void nyuryokujyunkoutiku(char g){
char i,j,k,h,jh,ih;
char min=100;
k=0;
for(i=0;i<9;i++){
for(j=0;j<9;j++){
if(a[i][j]==0){
h=1;
if(g>0){
for(k=0;k<g;k++){
if(j==zx[k] && i==zy[k]){ //この辺の記述にも問題がありました。以降赤が訂正箇所。
h=0;
break;
}
}
}
if(h==1){
if(b[i][j]<min){
min=b[i][j];
ih=i;
jh=j;
}
}
}
}
}
zx[g]=jh;
zy[g]=ih;
if(g+1<cn){
nyuryokujyunkoutiku(g+1);
}
//以降は、動作が正常であることが確認され次第削除
else{
for(i=0;i<4;i++)for(j=0;j<13;j++)dataGridView1[j,4*i+28]->Value=L"*";
for(i=0;i<4;i++)for(j=0;j<13;j++)dataGridView1[4*i,j+28]->Value=L"*";
for(i=0;i<cn;i++){
if(a[zy[i]][zx[i]]==0){
if(zx[i]<3)xx=zx[i];
if(zx[i]>2 && zx[i]<6)xx=zx[i]+1;
if(zx[i]>5 && zx[i]<9)xx=zx[i]+2;
if(zy[i]<3)yy=zy[i];
if(zy[i]>2 && zy[i]<6)yy=zy[i]+1;
if(zy[i]>5 && zy[i]<9)yy=zy[i]+2;
dataGridView1[xx+1,yy+29]->Value=i;
}
for(i=0;i<9;i++){
for(j=0;j<9;j++){
if(a[i][j]>0){
if(j<3)xx=j;
if(j>2 && j<6)xx=j+1;
if(j>5 && j<9)xx=j+2;
if(i<3)yy=i;
if(i>2 && i<6)yy=i+1;
if(i>5 && i<9)yy=i+2;
dataGridView1[xx+1,yy+29]->Value=L"/";
}
}
}
}
//ここまでが削除対象
}
}
・
・
・
こちらは難解です。難解な割には、実行速度は変わりません。しかし、将来のバージョンのために必要です。
このVer.2は、最初だけ問題構造解析とそれに基づく入力順の構築をしますが、
実は、リスト構造とそれに基づく入力順はセルに数字を入れる度に変わっていきます。
したがって、初回だけでなく毎回構造分析と入力順構築が必要です。
しかし、毎回全体構造解析をしていたのでは時間に無駄ですし、
実は入力順は、今回のように全体を構築する必要はありません。
f(g)のg番目だけの入力順が決まっていればよいのです。
数字を入れたことによって影響を受ける部分だけの部分構造解析
とその回だけの入力順構築が出来れば、問題を解く速度は著しく改善されます。
今回の難解なコードはその回だけの入力順構築に使うことが出来ます。
尚、コード例1の解説は第11話で、コード例2の解説は第12話と第13話で行います。
では第10話は何でしょうか。
そうです。
Ver.2の完成コードを載せることが第10話の任務です。
皆さん、コード例を見る前に数独問題解決ソフトVer.2を完成させましょう。
第8話へ 第10話へ
コード解説
第9話のコードは複雑ですね。
* | 6 | 5 | 3 | 5 | 4 | * | 5 | 5 |
3 | 5 | * | * | 5 | * | 5 | 4 | 5 |
5 | 7 | 6 | 5 | 6 | 5 | 8 | 6 | 6 |
5 | 7 | 6 | 3 | 6 | * | 5 | 6 | 5 |
4 | 7 | * | 1 | 4 | 4 | 5 | 6 | * |
4 | 5 | 4 | 3 | * | * | 5 | * | 4 |
3 | 4 | 4 | * | 4 | 3 | 5 | * | * |
* | 7 | 5 | 5 | 5 | 4 | 5 | 5 | 6 |
* | 6 | 4 | * | 5 | 4 | 5 | 5 | 5 |
実は、VBA版で数独問題解決ソフトVer.1(VBA版ではVer.1と命名)を作ったときには、複雑すぎて断念したコードです。
それで、第8話のコードで妥協して、本来作ろうと思ったコードが実現できればもっと速いバージョンが出来たはずだと残念に思っていたのです。
しかし、今回本来やりたかった方のコードが実現してみると、実行速度にたいした差はなく数%速くなっただけでした。
このようなことは、よくあることです。
プログラマーの見込み=直観が、正しくないということは。
ただし、プログラミングをしていく上で直観の果たす役割は重要です。
ときには、間違えることがあるとはいえ、直観がなければプログラムをすることは不可能です。
創造は、右脳の仕事だからです。
左脳は、右脳が立てた仮説を論理的に検証するだけです。
導きは、右脳の与える直観です。
* | 48 | 21 | 1 | 22 | 7 | * | 23 | 24 |
2 | 25 | * | * | 26 | * | 27 | 8 | 28 |
29 | 59 | 49 | 30 | 50 | 31 | 63 | 51 | 52 |
32 | 60 | 53 | 3 | 54 | * | 33 | 55 | 34 |
9 | 61 | * | 0 | 10 | 11 | 35 | 56 | * |
12 | 36 | 13 | 4 | * | * | 37 | * | 14 |
5 | 15 | 16 | * | 17 | 6 | 38 | * | * |
* | 62 | 39 | 40 | 41 | 18 | 42 | 43 | 57 |
* | 58 | 19 | * | 44 | 20 | 45 | 46 | 47 |
さて、見込み違いとはいえ今回のコードでも見事に番号割り振りに成功します。
どうしてうまくいくのでしょうか。
基本的な考え方は、関数の再帰的利用です。
なぜ、再帰的利用なのでしょうか。
63個の対象から、最小値(複数ある場合には最初に見つかった方)を見いだしそれに番号を割り振り、
それを対象から外した62個から最小値を見いだし番号割り振り対象から除外します。
次の、61個から同じことを繰り返します。
そうです。本質的には同じことを63回繰り返すのです。
同じことを繰り返すときに向いていたのは、関数の再帰的使用=関数の自己再帰でした。
VC++講義第1部へ
vb講義へ
VB講義基礎へ
初心者のための世界で一番わかりやすいVisual C++入門基礎講座
初心者のための世界で一番わかりやすいVisual
Basic入門基礎講座
初心者のための世界で一番わかりやすいVBA入門講義(基礎から応用まで)