第16講 関数の再帰的呼び出しによる魔方陣の作成
第4話 普遍版魔方陣自動生成プログラムコード解説その1
コード再掲

#include<iostream>
using namespace std;
using namespace System;
void f1(int g);
void f2();
int n,cn;
int a[10][10];
int main(){
  cout<<"何次魔方陣を作成させるのかキーボードから入力してください。"<<endl;
  cout<<"次数=";
  scanf("%d",&n);
  DateTime^ hj=DateTime::Now;
  cn=0;
  f1(0);
  cout<<n<<"次魔方陣が"<<cn<<"個できました。"<<endl;
  DateTime^ ow=DateTime::Now;
  TimeSpan sa=ow->Subtract(*hj);
  cout<<"計算時間は"<<sa.TotalSeconds<<"秒です。"<<endl;
}
void f1(int g){
  int i,j,h,w,x,y;
  y=g/n;
  x=g%n;
  for(i=1;i<n*n+1;i++){
    a[y][x]=i;
    h=1;
    if(g>0){
      for(j=0;j<g;j++){
        if(a[y][x]==a[j/n][j%n]){
          h=0;
          break;
        }
      }
    }
    if(h==1 && x==n-1){
      w=0;
      for(j=0;j<n;j++){
        w+=a[y][j];
      }
      if(w!=n*(n*n+1)/2)h=0;
    }
    if(h==1 && y==n-1){
      w=0;
      for(j=0;j<n;j++){
        w+=a[j][x];
      }
      if(w!=n*(n*n+1)/2)h=0;
    }
    if(h==1 && y==n-1 && x==0){
      w=0;
      for(j=0;j<n;j++){
        w+=a[j][n-1-j];
      }
      if(w!=n*(n*n+1)/2)h=0;
    }
    if(h==1 && y==n-1 && x==n-1){
      w=0;
      for(j=0;j<n;j++){
        w+=a[j][j];
      }
      if(w!=n*(n*n+1)/2)h=0;
    }
    if(h==1){
      if(g+1<n*n){
        f1(g+1);
       }
       else{
        cn++;
        f2();
        //if(cn==100)break;
       }
    }
    //if(cn==100)break;
  }
}
void f2(){
  int i,j;
  for(i=0;i<n;i++){
    for(j=0;j<n;j++){
      if(a[i][j]<10)cout<<" "<<a[i][j]<<" ";
      if(a[i][j]>=10)cout<<a[i][j]<<" ";
    }
    cout<<endl;
  }
  cout<<endl;
}

解説
  DateTime^ hj=DateTime::Now;

  DateTime^ hj;
  hj=DateTime::Now;
と同じです。宣言と初期化を同時に行っただけです。
まず、
  f1(0);
で一番外側の人形が呼び出されます。
n=3と入力される場合最大で人形は入れ子式に9つまで発生します。
n=4なら16個です。最大nの2乗個入れ子式に発生します。
一番外側の人形と比喩しているものは、
実際には一番目のセルです。
すなわち、セル識別番号g=0のセルです。

0 1 2 3
4 5 6 8
9 10 11 12
12 13 14 15

といっても上のイメージは正しくありません。正しくは、

0

です。1番外側の人形しか発生していなくて、その内側には人形はまだ存在していません。
まだ、入れ子式人形になっていないのです。
というより正確には、次元番号0の世界しか存在していません。
f1(0)がf1(1)を呼び出すとき、はじめてパラレルワールドが存在するようになるのです。
f1(0)は1番目の世界(1番外側の人形)、f1(1)は2番目の世界(外側から2番目の人形)、
f1(2)は3番目の世界(外側から3番目の人形)、・・・と対応します。
自分が自分を呼び出すといっても、次元の異なる自分です。
そして、その次元識別番号がgなのです。
順列のときと同じで次元識別番号=空間識別番号=セル識別番号とセルの中に入る数字を区別しないと訳がわからなくなります。

0
 

セル番号(セル識別番号)は、ビール瓶のラベルに相当し、
セルに入る数字は内容物であるビールに相当していたことを忘れないでください。
  y=g/n;
  x=g%n;
によって、空間識別は空間識別番号gから、(,x)の組み合わせに変わります。

  0 1 2 3
0        
1
2
3


  y=0/n=0;
  x=0%n=0;
ですから、for文
  for(i=1;i<n*n+1;i++){
    a[y][x]=i;
の最初のループによって

  0
0

    if(g>0){
      for(j=0;j<g;j++){
        if(a[y][x]==a[j/n][j%n]){
          h=0;
          break;
        }
      }
    }
    if(h==1 && x==n-1){
      w=0;
      for(j=0;j<n;j++){
        w+=a[y][j];
      }
      if(w!=n*(n*n+1)/2)h=0;
    }
    if(h==1 && y==n-1){
      w=0;
      for(j=0;j<n;j++){
        w+=a[j][x];
      }
      if(w!=n*(n*n+1)/2)h=0;
    }
    if(h==1 && y==n-1 && x==0){
      w=0;
      for(j=0;j<n;j++){
        w+=a[j][n-1-j];
      }
      if(w!=n*(n*n+1)/2)h=0;
    }
    if(h==1 && y==n-1 && x==n-1){
      w=0;
      for(j=0;j<n;j++){
        w+=a[j][j];
      }
      if(w!=n*(n*n+1)/2)h=0;
    }
のいずれにも抵触しませんので、
   if(h==1){
      if(g+1<n*n){
        f1(g+1);
       }
       else{
        cn++;
        f2();
        //if(cn==100)break;
       }
    }
が実行され、

  0 1
0

となります。


第3話へ 第5話へ

戻る

C言語 C++講義第1部へ
VB講義へ
VB講義基礎へ

vc++講義へ第1部へ
初心者のための世界で一番わかりやすいVisual C++入門基礎講座
初心者のための世界で一番わかりやすいVisual Basic入門基礎講座
初心者のための世界で一番わかりやすいVBA入門講義(基礎から応用まで)