所谓幻方,就是在一个nXn的正方形中,分别填上1到n*n的数字,使得每行每列以及对角线上的数字之和相等。比如,小学学过的九宫格就属于3阶幻方。幻方最早起源于中国。
对于幻方的求解,首先我们按照其阶数n(即一个边上有多少个格子)将其分为奇阶幻方和偶阶幻方,然后偶阶幻方又分为单偶幻方和双偶幻方。
奇阶幻方解法
奇阶幻方最简单的解法就是“罗伯法”,又称“楼梯法”。
具体步骤为:首先在第一行正中间的格子填1,然后就像走楼梯一样将2、3…n的数字依次填入右上角。这时可能会出现一个情况,就是超出了幻方的格子范围,当我们一直往右上角填的时候,如果是行超出了就行数加n,如果是列数超出了就列数减n,以保证数字始终在范围内。当填完1到n之后,开始填n+1到2*n。首先将n+1填到n的下面一格,然后再按走楼梯的方式填入n+2到2*n。如果中途出现超出范围的情况,同样按之前的方法处理。然后开始填2*n+1到3*n,首先将2*n+1填到2*n的下面一格,之后的都按之前的规律类推。
具体我们看下面的这个5阶幻方的例子:
首先,将第一行正中间填入1,然后2填在1的右上角。这时会发现,此时1的右上角超出格子了,我们可以将每列和每行看作一个环状。比如,第四列是一个连通的环,第四列第一个格子在向上走一格就到了第四列的最后一个格子,此时,这里填入2。然后2的右上角填入3,然后3的右上角填入4(同样的道理可将第三行看作环,那么4就填入了第三行的第一个格子),4的右上角填入5。这样就填完了前n个数,接下来我们填接下来的n个数,6直接填在5的下面一格,然后7填在6的右上角,8填在7的右上角,9填在8的右上角,10填在9的右上角。然后接下来的n个数也是如此,将11填在10的正下面,12填在11的右上角,剩下的依此类推。
双偶幻方解法
所谓双偶幻方即阶数n为4的倍数的幻方,这里用到的方法叫做“对称交换法”。
首先将1到n*n的数字,按行优先的顺序依次填满整个矩阵。
接下来将整个幻方完全划分成整数个4x4的大格子,然后如图
在每个4x4的大格子中都作这样的一个下标。最后,对于整个幻方(注意是整个)所有带有‘*’星号下标的数字进行一个统一的中心对称变换。
具体的例子我们看一个8阶幻方:
首先依次填入1到64,然后划分成一个个的4x4的大格子(这里我们用4种不同的颜色标出)。
然后我们标记相应的‘*’
最后对整体进行一个中心对称变换(简单地说,该格与中心的连线旋转180度)
结果为
单偶幻方解法
所谓单偶幻方就是阶数n为偶数但又不能被4整除的幻方,这里用到的方法叫做“象限对称交换法”。
首先将整个幻方分为四个象限
将1到n*n的数字也分成四组:1到n*n/4,n*n/4+1到n*n/2,n*n /2+1到3*n*n /4,3*n*n /4+1到n*n。
将这四组数分别按“罗伯法”填入到A、B、C、D四个象限中。
计算得到m=(n-2)/4。
将A象限正中的m格(即从A的最中间的格子开始从正中向右数m格)与D中相应部分(即对应的正中间的m格)平移互换。
然后A中另一部分(即不包括正中行的其他行,并且只换正中列以左的)也与D中相应部分(即对应处)平移互换。
C区中间m-1列(从中间列开始向右数共m-1列)与B中相应部分平移互换。
这里我们以10阶幻方为例:
首先我们按“罗伯法”填好A、B、C、D四个象限(注意分组填),如图。
然后A与D交换相应部分,C与B交换相应部分,这里用红色标出。
交换后为:
#include<stdio.h>
#define N 8 //阶数
int main()
{
int x,y,a[N][N]={0},i=0,j,n,m;
void change(int *a,int *b);
if(N%2==1) //奇阶幻方
{
for(j=0;j<N;j++)
{
if(j==0&&i==0)
{
x=0;
y=N/2;
a[x][y]=1;
}
else
{
a[x+1][y]=a[x][y]+1;
x=x+1;
}
for(i=1;i<N;i++)
{
x=x-1;y=y+1;
if(y>N-1){y=y-N;}
if(x<0){x=x+N;}
a[x][y]=i+1+N*j;
}
}
}
else
{
if(N%4==0) //双偶幻方
{
for(i=0;i<N;i++)
for(j=0;j<N;j++)
a[i][j]=i*N+j+1;
for(i=0;i<N/2;i++)
for(j=0;j<N;j++)
{
for(x=0,n=0;n<=N/4-1;n++)
{
if(((i!=j+n*4)&&(i!=j-n*4))&&((i+j+n*4!=N-1)&&(i+j-n*4!=N-1)))x++;
}
if(x==N/4)
change(&a[i][j],&a[N-i-1][N-j-1]);
}
}
else //单偶幻方
{
m=(N-2)/4;
for(j=0;j<N/2;j++) //A区
{
if(j==0&&i==0)
{
x=0;
y=N/4;
a[x][y]=1;
}
else
{
a[x+1][y]=a[x][y]+1;
x=x+1;
}
for(i=1;i<N/2;i++)
{
x=x-1;y=y+1;
if(y>N/2-1){y=y-N/2;}
if(x<0){x=x+N/2;}
a[x][y]=i+1+(N/2)*j;
}
}
j=N/2,i=N/2; //B区
for(j=N/2;j<N;j++)
{
if(j==N/2&&i==N/2)
{
x=N/2;
y=3*N/4;
a[x][y]=N*N/4+1;
}
else
{
a[x+1][y]=a[x][y]+1;
x=x+1;
}
for(i=N/2+1;i<N;i++)
{
x=x-1;y=y+1;
if(y>N-1){y=y-N/2;}
if(x<N/2){x=x+N/2;}
a[x][y]=i-N/2+N*N/4+1+(N/2)*(j-N/2);
}
}
j=0,i=N/2;
for(j=0;j<N/2;j++) //C区
{
if(j==0&&i==N/2)
{
x=0;
y=N*3/4;
a[x][y]=2*N*N/4+1;
}
else
{
a[x+1][y]=a[x][y]+1;
x=x+1;
}
for(i=N/2+1;i<N;i++)
{
x=x-1;y=y+1;
if(y>N-1){y=y-N/2;}
if(x<0){x=x+N/2;}
a[x][y]=i-N/2+2*N*N/4+1+(N/2)*j;
}
}
j=N/2,i=0;
for(j=N/2;j<N;j++) //D区
{
if(j==N/2&&i==0)
{
x=N/2;
y=N/4;
a[x][y]=3*N*N/4+1;
}
else
{
a[x+1][y]=a[x][y]+1;
x=x+1;
}
for(i=1;i<N/2;i++)
{
x=x-1;y=y+1;
if(y>N/2-1){y=y-N/2;}
if(x<N/2){x=x+N/2;}
a[x][y]=i+3*N*N/4+1+(N/2)*(j-N/2);
}
}
for(m=0;m<(N-2)/4;m++)
change(&a[N/4][N/4+m],&a[N/4+N/2][N/4+m]);
for(j=0;j<N/2;j++)
{
if(j==N/4)continue;
for(i=0;i<N/4;i++)
change(&a[j][i],&a[j+N/2][i]);
}
for(m=0;m<(N-2)/4-1;m++)
{
for(j=0;j<N/2;j++)
change(&a[j][3*N/4+m],&a[j+N/2][3*N/4+m]);
}
}
}
for(i=0;i<N;i++)
{
for(j=0;j<N;j++)
printf("%5d",a[i][j]);
printf("\n");
}
return 0;
}
void change(int *a,int *b)
{
int t;
t=*a;
*a=*b;
*b=t;
}
(PS:直接扒的大一时候写的代码,有些地方编程可能不合理'(*>﹏<*)′)
转载请标明出处,原文地址:https://blog.csdn.net/come_from_pluto