幻方构造方法及C语言实现

所谓幻方,就是在一个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

  • 23
    点赞
  • 81
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值