第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){
・・・
・・・
}
直交のチェックはどのようにしたら出来るのでしょうか。