第16講 関数の再帰的呼び出しによる3次・4次魔方陣の作成
第3話 仮屋崎さんの天才的方法の解説

        void g(char n){
           char i,j;
           char b[10][10];
           int cn=n;
           for(i=0;i<n;i++){
             for(j=0;j<n;j++){
               b[i][j]=-1;
             }
           }

          
for(i=0;i<n;i++){
             b[i][i]=i;
           }

           
for(i=0;i<n;i++){
             if(b[i][n-1-i]==-1){
               b[i][n-1-i]=cn;
               cn++;
             }
           }

           for(i=0;i<n;i++){
             for(j=0;j<n;j++){
               if(b[i][j]==-1){
                 b[i][j]=cn;
                 cn++;
               }
             }
           }

           for(i=0;i<n;i++){
             for(j=0;j<n;j++){
               x[b[i][j]]=j;
               y[b[i][j]]=i;
             }
           }

        }
では色別に順に解説していきましょう。
配列b[i][j]は、前話の「@座標から番号へ」の部分に相当します。
そして、 最初のピンクは準備です。
すべて配列の値を−1にしています。

-1 -1 -1 -1 -1
-1 -1 -1 -1 -1
-1 -1 -1 -1 -1
-1 -1 -1 -1 -1
-1 -1 -1 -1 -1


そして、以降で配列b[i][j]の値が−1のときのみ書き換えられるようにしてあります。
これは重複を防ぐために用意されています。
尚、紺では重複の可能性がないのでif(b[i][j]==-1)が用意されていません。
重複については以下の説明を見れば分かります。







           for(i=0;i<n;i++){
             b[i][i]=i;
           }

では

-1 -1 -1 -1
-1 1 -1 -1 -1
-1 -1 -1 -1
-1 -1 -1 -1
-1 -1 -1 -1

対角線に数字を入れていっています。
ここでは番号の重複は全く心配ありません。
そして、次の
           for(i=0;i<n;i++){
             if(b[i][n-1-i]==-1){
               b[i][n-1-i]=cn;
               cn++;
             }
           }

で逆対角線に順に番号を入れていきます。

-1 -1 -1
-1 1 -1 -1 -1
-1 -1 -1 -1
-1 -1 -1 -1
-1 -1 -1 -1



-1 -1 -1
-1 1 -1 -1
-1 -1 -1 -1
-1 -1 -1 -1
-1 -1 -1 -1










そして、注目の場所にやってきました。

-1 -1 -1
-1 1 -1 -1
-1 -1 -1 -1
-1 -1 -1 -1
-1 -1 -1 -1


ここではじめて
             if(b[i][n-1-i]==-1){
               b[i][n-1-i]=cn;
               cn++;
             }

が活きます。
b[2][2]には、−1以外の番号がすでに入っています。
b[2][2]=2です。
ここは番号がすでに入っているわけですから、
入れてはいけないわけです。
そのためにif文があります。
これが重複チェックの意味です。if文が働いて b[2][2]ではなにもせず、
次に進み、

-1 -1 -1
-1 1 -1 -1
-1 -1 -1 -1
-1 -1 -1
-1 -1 -1 -1


-1 -1 -1
-1 1 -1 -1
-1 -1 -1 -1
-1 -1 -1
-1 -1 -1













ここで注目すべきは、奇数次と偶数次に分ける必要がないということです。
もちろん偶数次の場合は、if文はすべて実行されます。偶数次の場合は、重複しませんでしたね。
さて、            
          for(i=0;i<n;i++){
             for(j=0;j<n;j++){
               if(b[i][j]==-1){
                 b[i][j]=cn;
                 cn++;
               }
             }
           }

についてはもうお分かりでしょう。一番最初において

-1 -1 -1
-1 1 -1 -1
-1 -1 -1 -1
-1 -1 -1
-1 -1 -1


if文のチェックが早速生かされます。
b[0][0]にはすでに番号0が入っています。
すなわち、b[0][0]=0です。
よって、
               if(b[i][j]==-1){
                 b[i][j]=cn;
                 cn++;
               }

は実行されません。
したがって、スルーされて次に進み

-1 -1
-1 1 -1 -1
-1 -1 -1 -1
-1 -1 -1
-1 -1 -1


となります。
以下

10 -1
-1 1 -1 -1
-1 -1 -1 -1
-1 -1 -1
-1 -1 -1



10 11
-1 1 -1 -1
-1 -1 -1 -1
-1 -1 -1
-1 -1 -1













と進んでいき、

10 11
12 1 -1 -1
-1 -1 -1 -1
-1 -1 -1
-1 -1 -1


再びif文のチェックが活きる場所に到達します。
b[1][1]=1です。
したがって、
               if(b[i][j]==-1){
                 b[i][j]=cn;
                 cn++;
               }

は実行されずスルーされます。



以下同様にして

10 11
12 1 13 14
15 16 17 18
19 20 21
22 23 24


と見事に重複せず番号付けが成功するのです。               











最後の
           for(i=0;i<n;i++){
             for(j=0;j<n;j++){
               x[b[i][j]]=j;
               y[b[i][j]]=i;
             }
           }

の働きは何でしょう。
ここでは「A番号から座標へ」をやっています。
各番号にx座標とy座標を対応させています。
トレースしてみましょう。
b[0][0]=0です。
ですから、ループの最初は
x[b[0][0]]=x[0]=0
y[b[0][0]]=y[0]=0
次は、 b[0][1]=9ですから
x[b[0][1]]=x[9]=1
y[b[0][1]]=y[9]=0
以下同様に
b[0][2]=10
x[b[0][2]]=x[10]=2
y[b[0][2]]=y[10]=0
b[0][3]=11
x[b[0][3]]=x[11]=3
y[b[0][3]]=y[11]=0
b[1][0]=12
x[b[1][0]]=x[12]=0
y[b[1][0]]=y[12]=1
b[1][1]=1
x[b[1][1]]=x[1]=1
y[b[1][1]]=y[1]=1
b[1][2]=13
x[b[1][2]]=x[13]=2
y[b[1][2]]=y[13]=1
     ・
     ・
     ・
b[4][3]=24
x[b[4][3]]=x[24]=3
y[b[4][3]]=y[24]=4
b[4][4]=4
x[b[4][4]]=x[4]=4
y[b[4][4]]=y[4]=4

どうです。とってもシャープな方法ですよね。
番号から座標へとなっていますね。
仮屋崎さんは天才です。



第17講(再講義)第2話へ 第17講(旧講義)第8話へ


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