第16講 関数の再帰的呼び出しによる3次・4次魔方陣の作成
第4話 ソース解説その1

さて、いよいよソースの解説です。

#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;
                 }
               }
             }
          }
  };
}

まず、
           x=g%n;
           y=g/n;

が1次元セル(枠)番号gを2次元に配置するために必要なものです。
g%nはgをnで割ったときの余り、g/nはgをnで割ったときの商ですが、yはint型なので小数部分は切り捨てられます。
n=4の場合でトレースしてみましょう。

g=0のときx=0%4=0、y=0/4=0より、a[y][x]はa[0][0]です。
g=1のときx=1%4=1、y=0/4=0より、a[y][x]はa[0][1]です。
g=2のときx=2%4=2、y=0/4=0より、a[y][x]はa[0][2]です。
g=3のときx=3%4=3、y=3/4=0より、a[y][x]はa[0][3]です。
g=4のときx=4%4=0、y=4/4=1より、a[y][x]はa[1][0]です。
g=5のときx=5%4=1、y=5/4=1より、a[y][x]はa[1][1]です。
g=6のときx=6%4=2、y=6/4=1より、a[y][x]はa[1][2]です。
g=7のときx=7%4=3、y=7/4=0より、a[y][x]はa[1][3]です。
g=8のときx=8%4=0、y=8/4=2より、a[y][x]はa[2][0]です。
g=9のときx=9%4=1、y=9/4=2より、a[y][x]はa[2][1]です。
g=10のときx=10%4=2、y=10/4=2より、a[y][x]はa[2][2]です。
g=11のときx=11%4=3、y=11/4=2より、a[y][x]はa[2][3]です。
g=12のときx=12%4=0、y=12/4=3より、a[y][x]はa[3][0]です。
g=13のときx=13%4=1、y=13/4=3より、a[y][x]はa[3][1]です。
g=14のときx=14%4=2、y=14/4=3より、a[y][x]はa[3][2]です。
g=15のときx=15%4=3、y=15/4=3より、a[y][x]はa[3][3]です。

結局gが次のように割り振られています。

10 11
12 13 14 15

赤の番号はx、濃紺の番号はy、ピンクの番号はgに対応)

gが2次元に配置し直されていることが分かります。

次の
                 xx=j%n;
                 yy=j/n;
ももうお分かりでしょう。これも番号j(<g)を2次元に割り振っています。

10 11
12 13 14 15

赤の番号はxx、紺の番号はyy、水色の番号はjに対応)

つまり、
             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;
                 }
               }
             }

は、例えば、g=10なら

10 11
12 13 14 15

0,1,2,3,4,5,6,7,8,9と比較して同じものがないかチェックしているのです。
同じ数字が入ってはいけないことは、魔方陣や順列の基本的な条件でした。
同じものがあるとき、h=0となってしまって
             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;
                 }
               }
             }
はすべて実行されず、iはi++によって1つ進みます。すなわち、a[y][x]=i+1も1つ進みます。
重複がなくなるまでa[y][x]=i+1は進んでいくのです。
このようにして数字の重複はなくなります。


第11講第6話へ
 第12講第1話へ 第14講第10話へ 第15講第10話へ 第16講第3話へ 第16講第5話へ





VC++講義第1部へ
vb講義へ
VB講義基礎へ
初心者のための世界で一番わかりやすいVisual C++入門基礎講座
初心者のための世界で一番わかりやすいVisual Basic入門基礎講座