第20講 魔方陣汎用的生成プログラムVer.3=末項確定法
第5話 Ver.3(末項確定法)コード解説その2
解説続き
では
if(y[g]==n-1 && x[g]==n-1){
int i,w=0;
for(i=0;i<n-1;i++)w+=m[i][i];
int sa;
sa=n*(n*n+1)/2-w;
if(sa<=0 || sa>n*n)return;
for(i=0;i<g;i++)if(sa==m[y[i]][x[i]])return;
m[y[g]][x[g]]=sa;
f(g+1);
if(cn>9)return;
return;
}
においては何をしているのでしょうか。
int i,w=0;
for(i=0;i<n-1;i++)w+=m[i][i];
では、
y | ||||||||||||
↓ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ← | x |
0 | ||||||||||||
1 | ||||||||||||
2 | ||||||||||||
3 | ||||||||||||
4 | ||||||||||||
5 | ||||||||||||
6 | ||||||||||||
7 | ||||||||||||
8 | ||||||||||||
9 |
ピンク色直前までの対角線の合計を計算しています。
そして、
int sa;
sa=n*(n*n+1)/2-w;
では魔方陣の行・列・対角線の合計との差を取っています。
もし、6次魔方陣であるなら
n*(n*n+1)/2=6*(6*6+1)/2=111
との差です。
if(sa<=0 || sa>n*n)return;
そして、その差が0以下であったりn*n(6次の場合は36)を超えていたら、ひとつ手前のセル
y | ||||||||||||
↓ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ← | x |
0 | ||||||||||||
1 | ||||||||||||
2 | ||||||||||||
3 | ||||||||||||
4 | ||||||||||||
5 | ||||||||||||
6 | ||||||||||||
7 | ||||||||||||
8 | ||||||||||||
9 |
に戻るように命令しています。
のセルに入れることのできる数字は
1から36までの数字ですから、
差が0以下または36より大きいなら、
ピンクのセルに何を入れても、
合計をn*(n*n+1)/2にすることは不可能ですから、
より前の世界(入れ子式人形の外側の人形)に戻ってやり直すしかないわけです。
さて、
for(i=0;i<g;i++)if(sa==m[y[i]][x[i]])return;
の働きは何でしょうか。たとえば、
7 | 2 | 2 | 5 | 0 | 4 | 5 | 5 | 9 | 7 |
7 | 12 | 7 | 6 | 1 | 3 | 5 | 4 | 6 | 2 |
3 | 1 | 29 | 8 | 4 | 7 | 6 | 2 | 0 | 5 |
3 | 3 | 1 | 39 | 7 | 3 | 4 | 5 | 6 | 9 |
5 | 4 | 1 | 7 | 47 | 2 | 4 | 3 | 9 | 9 |
9 | 7 | 5 | 1 | 6 | 63 | 3 | 7 | 3 | 4 |
2 | 8 | 5 | 2 | 2 | 8 | 72 | 8 | 0 | 2 |
0 | 2 | 1 | 6 | 7 | 8 | 9 | 86 | 8 | 1 |
3 | 8 | 5 | 0 | 8 | 9 | 0 | 8 | 93 | 0 |
7 | 6 | 9 | 6 | 9 | 1 | 1 | 0 | 0 |
の場合差は60です。しかし、
のセルに60を入れてしまうと、
数字が重複してはならないという魔方陣の基本のルールに反してしまいます。
するとこの場合にもひとつ手前の世界
に戻りやり直すしかないので、return;命令が遂行されます。
両検査
if(sa<=0 || sa>n*n)return;
for(i=0;i<g;i++)if(sa==m[y[i]][x[i]])return;
に合格してはじめて
のセルに数字を入れることができます。
例えば
7 | 2 | 2 | 5 | 0 | 4 | 5 | 5 | 9 | 7 |
7 | 12 | 7 | 6 | 1 | 3 | 5 | 4 | 6 | 2 |
3 | 1 | 29 | 8 | 4 | 7 | 6 | 2 | 0 | 5 |
3 | 3 | 1 | 39 | 7 | 3 | 4 | 5 | 6 | 9 |
5 | 4 | 1 | 7 | 47 | 2 | 4 | 3 | 9 | 9 |
9 | 7 | 5 | 1 | 6 | 63 | 3 | 7 | 3 | 4 |
2 | 8 | 5 | 2 | 2 | 8 | 72 | 8 | 0 | 2 |
0 | 2 | 1 | 6 | 7 | 8 | 9 | 86 | 8 | 1 |
3 | 8 | 5 | 0 | 8 | 9 | 0 | 8 | 93 | 0 |
7 | 6 | 9 | 6 | 9 | 1 | 1 | 0 | 0 |
であれば7+12+29+39+47+63+72+86+93=448
で10次魔方陣の行・列・対角線の合計505との差は57で、重複がなく
7 | 2 | 2 | 5 | 0 | 4 | 5 | 5 | 9 | 7 |
7 | 12 | 7 | 6 | 1 | 3 | 5 | 4 | 6 | 2 |
3 | 1 | 29 | 8 | 4 | 7 | 6 | 2 | 0 | 5 |
3 | 3 | 1 | 39 | 7 | 3 | 4 | 5 | 6 | 9 |
5 | 4 | 1 | 7 | 47 | 2 | 4 | 3 | 9 | 9 |
9 | 7 | 5 | 1 | 6 | 63 | 3 | 7 | 3 | 4 |
2 | 8 | 5 | 2 | 2 | 8 | 72 | 8 | 0 | 2 |
0 | 2 | 1 | 6 | 7 | 8 | 9 | 86 | 8 | 1 |
3 | 8 | 5 | 0 | 8 | 9 | 0 | 8 | 93 | 0 |
7 | 6 | 9 | 6 | 9 | 1 | 1 | 0 | 0 | 57 |
対角線の合計も7+12+29+39+47+63+72+86+93+57=505
と条件を満たしています。それで、
f(g+1);
によって、晴れて
y | ||||||||||||
↓ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ← | x |
0 | ||||||||||||
1 | ||||||||||||
2 | ||||||||||||
3 | ||||||||||||
4 | ||||||||||||
5 | ||||||||||||
6 | ||||||||||||
7 | ||||||||||||
8 | ||||||||||||
9 |
の世界へと突き進むことができるのです。
結局
if(y[g]==n-1 && x[g]==n-1){
int i,w=0;
for(i=0;i<n-1;i++)w+=m[i][i];
int sa;
sa=n*(n*n+1)/2-w;
if(sa<=0 || sa>n*n)return;
for(i=0;i<g;i++)if(sa==m[y[i]][x[i]])return;
m[y[g]][x[g]]=sa;
f(g+1);
if(cn>9)return;
return;
}
の役割は、条件を満たさなければ前の世界に強制帰還させ、
条件を満たせば、次の世界へと跳躍させることにあるのです。
したがいまして、
int i,j,h,ii,iii;
ii=0;
if(n!=3 &&
n!=4){
Random r = new
Random(s);
ii=(int)(r.nextInt(n*n));
}
for(i=1;i<n*n+1;i++){
h=1;
iii=(t*i+ii)%(n*n)+1;
m[y[g]][x[g]]=iii;
for(j=0;j<g;j++){
if(m[y[g]][x[g]]==m[y[j]][x[j]]){
h=0;
}
}
if(h==1)f(g+1);
if(cn>9)return;
ow =
System.currentTimeMillis();
if((ow-hj)/1000>mn)return;
}
の部分には一切触れさせないのです。
要するに
のセルにおいて無意味なループ処理を行わないことによって、
スピードアップが図られているのです。
第4話へ 第6話へ
VB講義へ
VB講義基礎へ
vc++講義へ第1部へ
初心者のための世界で一番わかりやすいVisual C++入門基礎講座
初心者のための世界で一番わかりやすいVisual Basic入門基礎講座
初心者のための世界で一番わかりやすいVBA入門講義(基礎から応用まで)
初心者のための VC++による C言語 入門 C++ 入門
基礎から応用まで第1部
初心者のための VC++による C言語 入門 C++ 入門
基礎から応用まで第2部
初心者のための
VC++による C言語 入門 C++ 入門 基礎から応用まで第3部