第16講 関数の再帰的呼び出しによる3次・4次魔方陣の作成
第5話 ソース解説その2
#pragma endregion
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^
e) {
DateTime^ hj=DateTime::Now;
n=int::Parse(textBox1->Text);
s=0;
f(0);
int i;
array<String^>^ x=gcnew array<String^>(16);
x[9]=L"魔";x[10]=L"方";x[11]=L"陣";x[12]=L"総";x[13]=L"数";x[14]=L":";x[15]=s.ToString();
for(i=0;i<9;i++)x[i]=L"";
dataGridView1->Rows->Add(x);
DateTime^ ow=DateTime::Now;
TimeSpan sa=ow->Subtract(*hj);
x[10]=L"時";x[11]=L"間";x[12]=L"計";x[13]=L"則";x[14]=L":";x[15]=(sa.TotalSeconds).ToString();
for(i=0;i<10;i++)x[i]=L"";
dataGridView1->Rows->Add(x);
}
void f(char g){
if(s==10)return;
int i,j,k,h,x,y,xx,yy,wa;
array<String^>^ w=gcnew array<String^>(16);
x=g%n;
y=g/n;
for(i=0;i<n*n;i++){
a[y][x]=i+1;
h=1;
if(g>0){
for(j=0;j<g;j++){
xx=j%n;
yy=j/n;
if(a[y][x]==a[yy][xx]){
h=0;
break;
}
}
}
if(h==1){
if(x==n-1){
wa=0;
for(j=0;j<n;j++)wa+=a[y][j];
if(wa!=n*(n*n+1)/2)h=0;
}
}
if(h==1){
if(y==n-1){
wa=0;
for(j=0;j<n;j++)wa+=a[j][x];
if(wa!=n*(n*n+1)/2)h=0;
}
}
if(h==1){
if(x==n-1 && y==n-1){
wa=0;
for(j=0;j<n;j++)wa+=a[j][j];
if(wa!=n*(n*n+1)/2)h=0;
}
}
if(h==1){
if(x==0 && y==n-1){
wa=0;
for(j=0;j<n;j++)wa+=a[n-j-1][j];
if(wa!=n*(n*n+1)/2)h=0;
}
}
if(h==1){
if(g<n*n-1){
f(g+1);
}
else{
for(j=0;j<n;j++){
for(k=0;k<n;k++){
w[k]=(a[j][k]).ToString();
}
dataGridView1->Rows->Add(w);
}
for(j=0;j<16;j++)w[j]=L"";
dataGridView1->Rows->Add(w);
s++;
if(s==10)return;
}
}
}
}
};
}
では残っている紺、ピンク、赤、濃紺の部分の解説をしましょう。
まず、
if(h==1){
if(x==n-1){
wa=0;
for(j=0;j<n;j++)wa+=a[y][j];
if(wa!=n*(n*n+1)/2)h=0;
}
}
では何をしているかと言いますと、
0 | 1 | 2 | 3 | |
0 | 0 | 1 | 2 | 3 |
1 | 4 | 5 | 6 | 7 |
2 | 8 | 9 | 10 | 11 |
3 | 12 | 13 | 14 | 15 |
(黄色の番号はg、赤色の番号はx、濃紺の番号はyに対応)
n=4の場合でトレースしていますので、x=n-1=3のとき、合計waがn*(n*n+1)/2でないかどうか調べています。
waはfor(j=0;j<n;j++)wa+=a[y][j];のfor文から横の合計であることが分かります。
n*(n*n+1)/2は4×(4×4+1)÷2=34です。これは4次魔方陣の横・縦・対角線の合計に一致します。
なぜなら方陣全体の合計は1+2+3+・・・+16=16×(16+1)÷2=4×4×(4×4+1)÷2ですから、
1行当たりの合計はそれを4で割った4×(4×4+1)÷2すなわち34です。
今、n=4の場合で説明しましたが、nのままでも同様な説明ができます。
n次方陣全体の合計はn×n×(n×n+1)÷2であり、1行当たりの合計はそれをnで割ったn×(n×n+1)÷2すなわちn*(n*n+1)/2です。
つまり紺
if(h==1){
if(x==n-1){
wa=0;
for(j=0;j<n;j++)wa+=a[y][j];
if(wa!=n*(n*n+1)/2)h=0;
}
}
でやっていることは、横の合計が34に等しくないならhを0にしなさいということなのです。
hが0になれば、もちろん
if(h==1){
if(g<n*n-1){
f(g+1);
}
else{
for(j=0;j<n;j++){
for(k=0;k<n;k++){
w[k]=(a[j][k]).ToString();
}
dataGridView1->Rows->Add(w);
}
for(j=0;j<16;j++)w[j]=L"";
dataGridView1->Rows->Add(w);
s++;
if(s==10)return;
}
}
}
の部分は実行されずi++によって、iは1つ進みます。
具体的なトレースは次話に回すことにして、次のピンク
if(h==1){
if(y==n-1){
wa=0;
for(j=0;j<n;j++)wa+=a[j][x];
if(wa!=n*(n*n+1)/2)h=0;
}
}
について説明しましょう。
0 | 1 | 2 | 3 | |
0 | 0 | 1 | 2 | 3 |
1 | 4 | 5 | 6 | 7 |
2 | 8 | 9 | 10 | 11 |
3 | 12 | 13 | 14 | 15 |
(黄色の番号はg、赤色の番号はx、濃紺の番号はyに対応)
y=n-1=3ですから、
3 | 12 | 13 | 14 | 15 |
の場合の相当します。
つまり列の一番下に来たら
wa=0;
for(j=0;j<n;j++)wa+=a[j][x];
if(wa!=n*(n*n+1)/2)h=0;
が実行されるわけです。for(j=0;j<n;j++)wa+=a[j][x];では縦合計を計算しています。
したがって、if(wa!=n*(n*n+1)/2)h=0;においては縦合計が34でないならhを0にせよの命令が実施されています。
hが0なら
if(h==1){
if(g<n*n-1){
f(g+1);
}
else{
for(j=0;j<n;j++){
for(k=0;k<n;k++){
w[k]=(a[j][k]).ToString();
}
dataGridView1->Rows->Add(w);
}
for(j=0;j<16;j++)w[j]=L"";
dataGridView1->Rows->Add(w);
s++;
if(s==10)return;
}
}
}
は実行されず、iはインクリメント(1つ進む)されます。
さて、それでは濃紺
if(h==1){
if(x==n-1 && y==n-1){
wa=0;
for(j=0;j<n;j++)wa+=a[j][j];
if(wa!=n*(n*n+1)/2)h=0;
}
}
では何がなされているのでしょうか。x=3かつy=3の場合とは、
0 | 1 | 2 | 3 | |
0 | 0 | 1 | 2 | 3 |
1 | 4 | 5 | 6 | 7 |
2 | 8 | 9 | 10 | 11 |
3 | 12 | 13 | 14 | 15 |
(黄色の番号はg、赤色の番号はx、濃紺の番号はyに対応)
15 |
対角線の一番下の場合に相当します。
ここに来たら、
wa=0;
for(j=0;j<n;j++)wa+=a[j][j];
if(wa!=n*(n*n+1)/2)h=0;
が実行されるわけです。今回のfor(j=0;j<n;j++)wa+=a[j][j];では何の合計をしているかと申しますと、
0 | 1 | 2 | 3 | |
0 | 0 | 1 | 2 | 3 |
1 | 4 | 5 | 6 | 7 |
2 | 8 | 9 | 10 | 11 |
3 | 12 | 13 | 14 | 15 |
対角線を合計しています。
a[0][0]、 a[1][1]、 a[2][2]、 a[3][3]
がひだりの対角線ピンクに対応していることをご確認下さい。
ですから、濃紺においては対角線の合計が34でないなら、
hを0にしなさいという命令が行われているのです。
最後の赤
if(h==1){
if(x==0 && y==n-1){
wa=0;
for(j=0;j<n;j++)wa+=a[n-j-1][j];
if(wa!=n*(n*n+1)/2)h=0;
}
}
が難解です。ですが、皆さんはもう予想がついていますね。そうです。
0 | 1 | 2 | 3 | |
0 | 0 | 1 | 2 | 3 |
1 | 4 | 5 | 6 | 7 |
2 | 8 | 9 | 10 | 11 |
3 | 12 | 13 | 14 | 15 |
(黄色の番号はg、赤色の番号はx、濃紺の番号はyに対応)
12 |
逆対角線の一番下の場合に相当します。
なぜならx=0かつy=3ですから。逆対角線の一番下に来ると
wa=0;
for(j=0;j<n;j++)wa+=a[n-j-1][j];
if(wa!=n*(n*n+1)/2)h=0;
が実行されます。a[n-j-1][j]に皆さんは頭を悩ませたことでしょう。
いったいなんでしょうか。トレースしてみましょう。
n=4ですから
j=0のとき、a[4-j-1][j]=a[3][0]です。
j=1のとき、a[4-j-1][j]=a[2][1]です。
j=2のとき、a[4-j-1][j]=a[1][2]です。
j=3のとき、a[4-j-1][j]=a[0][3]です。
では、 a[3][0]、 a[2][1]、 a[1][2]、 a[0][3]とは何でしょうか。
0 | 1 | 2 | 3 | |
0 | 0 | 1 | 2 | 3 |
1 | 4 | 5 | 6 | 7 |
2 | 8 | 9 | 10 | 11 |
3 | 12 | 13 | 14 | 15 |
そうです。逆対角線を下から上に上がっているのです。
もし、逆対角線を上から下に下がりたいならa[j][n-j-1]
とすればよろしいということは、お分かりでしょうか。
j=0のとき、a[j][4-j-1]=a[0][3]です。
j=1のとき、a[j][4-j-1]=a[1][2]です。
j=2のとき、a[j][4-j-1]=a[2][1]です。
j=3のとき、a[j][4-j-1]=a[3][0]です。
下から上に上がって行くにしろ、上から下に下がって行くにしろ
結局は逆対角線の合計を計算していることに変わりはありません。
結局赤でやっていることは逆対角線の合計が34でないならhを0にしなさいの命令なのです。
では、皆さん次話以降では具体的にトレースしてみましょう。
第11講第6話へ 第12講第1話へ 第14講第10話へ 第15講第10話へ 第16講第4話へ 第16講第6話へ
VC++講義第1部へ
vb講義へ
VB講義基礎へ
初心者のための世界で一番わかりやすいVisual C++入門基礎講座
初心者のための世界で一番わかりやすいVisual
Basic入門基礎講座