第24講 特殊種法で魔方陣を超高速に自動生成する
第9話 直交とは?

5 3 2 1 4
3 4 1 5 2
1 2 3 4 5
4 1 5 2 3
2 5 4 3 1


1 5 4 2 3
3 4 1 5 2
5 3 2 4 1
2 1 5 3 4
4 2 3 1 5

を結合させてしまうと

21 15 9 2 18
13 19 1 25 7
5 8 12 19 21
17 1 25 8 14
9 22 18 11 5

と問題のある「魔方陣もどき」がどうして出来てしまうのでしょうか。
理由は、種の方にも色を付けてみると一目瞭然です。

5 3 2 1 4
3 4 1 5 2
1 2 3 4 5
4 1 5 2 3
2 5 4 3 1


1 5 4 2 3
3 4 1 5 2
5 3 2 4 1
2 1 5 3 4
4 2 3 1 5

同色同士の組み合わせが、同じ組み合わせになっています。
例えば、
黄色のセルの組み合わせを見ると
2つとも同じ組(2,4)となっています。
前半から1を引いて5をかけてそれに後半を足すのですから、
(2-1)×5+4=9
の計算からいずれも同じ

9

になってしまうのは当然なのです。
同じ組が一つでも存在してしまえば、
数字の重複が必ず発生します。
ですから、直交しないとは同じ組が1個以上存在することですし、
直交するとは、同じ組が一つも存在しないということです。
同じ組が存在しないとき、
(1,1),(1,2),(1,3),(1,4),(1,5)
(2,1),(2,2),(2,3),(2,4),(2,5)
(3,1),(3,2),(3,3),(3,4),(3,5)
(4,1),(4,2),(4,3),(4,4),(4,5)
(5,1),(5,2),(5,3),(5,4),(5,5)
組み合わせは25通り存在して、
前半からは1を引いて5をかけて、後半を足すという計算規則を適用すれば
 1, 2, 3, 4, 5
 6, 7, 8, 9,10
11,12,13,14,15
16,17,18,19,20
21,22,23,24,25
「1から25までの数字が1つずつ」
という魔方陣の基本ルールを守れることになるのです。

さて、
#include<iostream>
using namespace std;
int f(int g,int m[10][10],int n,int* p,int* q,int cn);
void h(int m[10][10],int n);
void zh(int n,int *p,int *q);
void sy(int m[10][10],int n);
void main(){
   int m[10][10],n,t=0;
   while(1){
     cout<<"何次魔方陣種を自動生成しますか。"<<endl;
     t=1;
     if(n!=6)break; else cout<<"6以外の数字を入れて下さい。"<<endl;
   }
   if(t==1)scanf("%d",&n);
   sy(m,n);
   int p[100],q[100];
   zh(n,p,q);
   cout<<n<<"次魔方陣種が"<<endl<<f(0,m,n,p,q,0)<<"個できました。"<<endl;;
   cout<<"プロジェクト終了"<<endl;
}
void zh(int n,int *p,int *q){
   int i,j,cn;
   int a[10][10];
   for(i=0;i<n;i++)
     for(j=0;j<n;j++)
        a[i][j]=-1;
   for(i=0;i<n;i++)a[i][i]=i;
   cn=n;
   for(i=0;i<n;i++){
     if(a[i][n-1-i]==-1){
        a[i][n-1-i]=cn;
        cn++;
     }
   }
   for(i=0;i<n;i++){
     for(j=0;j<n;j++){
         if(a[i][j]==-1){
           a[i][j]=cn;
           cn++;
         }
     }
   }
   for(i=0;i<n;i++){
     for(j=0;j<n;j++){
        p[a[i][j]]=j;
        q[a[i][j]]=i;
     }
   }
}
int f(int g,int m[10][10],int n,int* p,int* q,int cn){
   int i,j,x,y;
   x=p[g];
   y=q[g];
   for(i=1;i<=n;i++){
     m[y][x]=i;
     if(g>0){
        if(g<n){
          for(j=0;j<g;j++){
             if(m[j][j]==m[y][x])goto tobi;
          }
        }
     }
     if(x==n-1-y && x!=y){
       for(j=0;j<y;j++){
          if(m[j][n-1-j]==m[y][x])goto tobi;
       }
       if(m[x][x]==m[y][x])goto tobi;
       if(m[y][y]==m[y][x])goto tobi;
     }
     if(x!=y && x!=n-1-y){
        for(j=0;j<x;j++){
          if(m[y][j]==m[y][x])goto tobi;
        }
        for(j=0;j<y;j++){
          if(m[j][x]==m[y][x])goto tobi;
        }
        if(m[y][y]==m[y][x])goto tobi;
        if(m[x][x]==m[y][x])goto tobi;
        if(m[y][n-1-y]==m[y][x])goto tobi;
        if(m[n-1-x][x]==m[y][x])goto tobi;
     }
     if(g+1<n*n){

        cn=f(g+1,m,n,p,q,cn);
        //if(cn==100)return(cn);
     }
     else{
        h(m,n);
        cout<<endl<<endl;
        //if(cn==100)return(cn);
        cn++;
     }
     tobi:;
   }
   return(cn);
}
void h(int m[10][10],int n){
   int i,j;
   for(i=0;i<n;i++){
     for(j=0;j<n;j++){
        //if(m[i][j]<10)cout<<"0"<<m[i][j]<<" ";
        cout<<m[i][j]<<" ";
     }
     cout<<endl;
   }
}
においては、特殊種を作る関数はfのみになっていましたが、
魔方陣を生成するには魔方陣種を2つ組み合わせなければなりませんから、
特殊種を作る関数をf1とf2の2つにします。
f1とf2の基本構造は同じですが、
f2の方は今作っている種が、
f1で作った種と直交しているかどうかをチェックしながら進めなければなりません。
そして、もし直交していないことが分かったら、
次ぎに進むようにしなければなりません。
#include<iostream>
using namespace std;
int f1(int g,int m1[10][10],int m2[10][10],int n,int* p,int* q,int cn);
int f2(int g,int m1[10][10],int m2[10][10],int n,int* p,int* q,int cn);
void h(
int m1[10][10],int m2[10][10],int n);
void zh(int n,int *p,int *q);
void sy(int m1[10][10],int n); //mainで初期化
void sy1(
int m2[10][10],int a[10][10],int n); //f1で初期化
void main(){
   int
m1[10][10],m2[10][10],n,t=0;
    ・・・
    ・・・
   cout<<n<<"次魔方陣種が"<<endl<<
f1(0,m1,m2,n,p,q,0)<<"個できました。"<<endl;;
   cout<<"プロジェクト終了"<<endl;
}
void zh(int n,int *p,int *q){
    ・・・
    ・・・
}
void sy(int m1[10][10],int m2[10][10],int a[10][10],int n){
    ・・・
    ・・・
}
void sy1(int m2[10][10],int a[10][10],int n){
    ・・・
    ・・・

}
int f1(int g,
int m1[10][10],int m2[10][10],int n,int* p,int* q,int cn){
    ・・・
    ・・・
     if(g+1<n*n){
        cn=
f1(g+1,m1,m2,n,p,q,cn);
     }
     else{
        cn=f2(0,m1,m2,n,p,q,cn);
     }
     tobi:;
   }
   return(cn);
}
int f2(int g,int m1[10][10],int m2[10][10],int n,int* p,int* q,int cn){
    ・・・
    ・・・
     if(g+1<n*n){
        cn=f2(g+1,m1,m2,n,p,q,cn);
        //if(cn==100)return(cn);
     }
     else{
        h(m1,m2,n);
        cout<<endl<<endl;
        //if(cn==100)return(cn);
        cn++;
     }
     tobi:;
   }
   return(cn);
}

void h(
int m1[10][10],int m2[10][10],int n){
    ・・・
    ・・・

}


直交のチェックはどのようにしたら出来るのでしょうか。

第8話へ 第10話へ

a

eclipse c++ 入門講義第1部へ

eclipse c++ 入門講義第2部へ


魔方陣 数独で学ぶ VBA 入門
数独のシンプルな解き方・簡単な解法の研究
VB講義へ
VB講義基礎へ
初心者のための世界で一番わかりやすいVisual C++入門基礎講座
初心者のための世界で一番わかりやすいVisual Basic入門基礎講座
初心者のための世界で一番わかりやすいVBA入門講義(基礎から応用まで)
初心者のための VC++による C言語 C++ 入門 基礎から応用まで第1部
eclipse java 入門
java 入門 サイト 基礎から応用まで
VC++ C言語 C++ 入門 初心者 基礎から応用まで
本サイトトップへ