第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の方です。
今回はこちらを解説していきましょう。

10 11
12 13
14 15

 

  












赤の番号は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入門基礎講座