第17講 関数の再帰的呼び出しによる3次・4次魔方陣ソフトの改良
第2話 枠番号付けの改良
どうです。皆さん、頭が爆発しそうになりませんでしたか。
超ド級の難問ですよね。
解答例を示しましょう。
#pragma once
int n;
int a[10][10];
int x[100],y[100];
int s;
・
・
・
#pragma endregion
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^
e) {
DateTime^ hj=DateTime::Now;
n=int::Parse(textBox1->Text);
s=0;
if(n%2==1)g1(n);
if(n%2==0)g2(n);
f(0);
・
・
・
}
void g1(char n){
char i,j,m;
m=n/2;
for(i=0;i<n*n;i++){
if(i<n){
x[i]=i;
y[i]=i;
}
else if(i<2*n-1){
x[i]=2*n-i-1;
y[i]=i-n;
if(x[i]<=y[i]){
x[i]--;
y[i]++;
}
}
else if(i<2*n+(n*n-3*n)/2){
x[i]=(i-2*n+1)%(n-2);
y[i]=(i-2*n+1)/(n-2);
if(x[i]>=y[i])x[i]++;
if(x[i]>=n-y[i]-1)x[i]++;
}
else if(i<3*n-1+(n*n-3*n)/2){
x[i]=i-2*n-(n*n-3*n)/2;
y[i]=m;
if(x[i]>=y[i])x[i]++;
}
else{
x[i]=(i-2*n)%(n-2);
y[i]=(i-2*n)/(n-2);
if(x[i]>=n-y[i]-1)x[i]++;
if(x[i]>=y[i])x[i]++;
}
}
}
void g2(char n){
char i,m;
m=n/2;
for(i=0;i<n*n;i++){
if(i<n){
x[i]=i;
y[i]=i;
}
else if(i<2*n){
x[i]=2*n-i-1;
y[i]=i-n;
}
else if(i<(m+1)*n){
x[i]=(i-2*n)%(n-2);
y[i]=(i-2*n)/(n-2);
if(x[i]>=y[i])x[i]++;
if(x[i]>=n-y[i]-1)x[i]++;
}
else{
x[i]=(i-2*n)%(n-2);
y[i]=(i-2*n)/(n-2);
if(x[i]>=n-y[i]-1)x[i]++;
if(x[i]>=y[i])x[i]++;
}
}
}
・
・
・
どうしてこれで思った通りの番号がつけられるのか、時間をかけて説明していきましょう。
簡単なのは偶数次の場合のg2の方です。
今回はこちらを解説していきましょう。
0 | 1 | 2 | 3 | |
0 | 0 | 8 | 9 | 4 |
1 | 10 | 1 | 5 | 11 |
2 | 12 | 6 | 2 | 13 |
3 | 7 | 14 | 15 | 3 |
(赤の番号はx[i]、濃紺の番号はy[i]、ピンクの番号は枠番号gに対応)
n=4の場合でトレースしてみます。
するとfor(i=0;i<n*n;i++)は0から15までの範囲でループします。
0から15がもちろん上の表のピンクの番号に相当するわけです。
if(i<n){
x[i]=i;
y[i]=i;
}
についてはお分かりでしょう。n=3までは、列番号x[i]と行番号y[i]はiと同じです。
だから、x[0]=0、y[0]=0、x[1]=1、y[1]=1として行けばよいわけです。
次の
else if(i<2*n){
x[i]=2*n-i-1;
y[i]=i-n;
}
はでi<nなくてi<2*nであるわけですから、結局iが4以上で7以下の範囲が対象になっています。
動きを具体的に追ってみましょう。
i=4のとき、x[i]=x[4]=2×4-4-1=3、y[i]=y[4]=4-n=4-4=0
i=5のとき、x[i]=x[5]=2×4-5-1=2、y[i]=y[5]=5-n=5-4=1
i=6のとき、x[i]=x[6]=2×4-6-1=1、y[i]=y[6]=6-n=6-4=2
i=4のとき、x[i]=x[7]=2×4-7-1=0、y[i]=y[7]=7-n=7-4=3
で思惑通り動いていることが分かります。
ミソは、x[i]=2*n-i-1;にあります。x[i]は3から減っていかなければならないのです。
さらに、
else if(i<(m+1)*n){
x[i]=(i-2*n)%(n-2);
y[i]=(i-2*n)/(n-2);
if(x[i]>=y[i])x[i]++;
if(x[i]>=n-y[i]-1)x[i]++;
}
はi<2*nでなくてi<(m+1)*nでして、mはm=n/2;4の半分で2ですから、該当するiは8以上11以下となります。
再び具体的にトレースしてみましょう。
i=8のとき、x[i]=x[8]=(8-2×n)%(n-2)=(8-2×4)%(4-2)=0、y[i]=y[8]=(8-2×n)/(n-2)=(8-2×4)÷(4-2)=0
x[8]≧y[8]ですから、x[i]++;が行われてx[8]=1
しかし、x[8]=1≧4-y[8]-1=3成り立ちませんから、x[i]++;1は行われませんので最終的にx[8]=1
i=9のとき、x[i]=x[9]=(9-2×n)%(n-2)=(9-2×4)%(4-2)=1、y[i]=y[9]=(9-2×n)/(n-2)=(9-2×4)÷(4-2)=0
x[8]≧y[8]ですから、x[i]++;が行われてx[9]=2
しかし、x[9]=2≧4-y[9]-1=3成り立ちませんから、x[i]++;は行われませんので最終的にx[9]=2
i=10のとき、x[i]=x[10]=(10-2×n)%(n-2)=(10-2×4)%(4-2)=0、y[i]=y[10]=(10-2×n)/(n-2)=(10-2×4)÷(4-2)=1
x[10]≧y[10]成り立ちませんので、x[i]++;は行われずx[10]=0のままです。
しかし、x[10]=0≧4-y[10]-1=2成り立ちませんから、x[i]++;は行われませんので最終的にx[10]=0
i=11のとき、x[i]=x[11]=(11-2×n)%(n-2)=(11-2×4)%(4-2)=1、y[i]=y[11]=(11-2×n)/(n-2)=(11-2×4)÷(4-2)=1
x[11]≧y[11]ですから、x[i]++;が行われてx[11]=2
しかし、x[11]=1≧4-y[11]-1=2成り立っていますから、x[i]++;は行われて最終的にx[10]=3
以上からうまく番号付けができていることが分かります。
では、皆さん最後の
else{
x[i]=(i-2*n)%(n-2);
y[i]=(i-2*n)/(n-2);
if(x[i]>=n-y[i]-1)x[i]++;
if(x[i]>=y[i])x[i]++;
}
をご自分でトレースしてみて見ましょう。
なぜうまくいってしまうかも考えてみて下さい。
答え合わせは次話で。
第11講第6話へ 第12講第1話へ 第17講第1話へ 第17講第3話へ
VC++講義第1部へ
vb講義へ
VB講義基礎へ
初心者のための世界で一番わかりやすいVisual C++入門基礎講座
初心者のための世界で一番わかりやすいVisual
Basic入門基礎講座