幻方的计算

/*
	幻方的计算:
	计算任意阶数幻方的各行、各列、各条对角线上所有数的和的公式为:sum=n*(n^2+1)/2  n为阶数
	幻方分为奇阶幻方和偶阶幻方
	一、当n为奇数时称为奇阶幻方
		1、Merzirac法生成奇阶幻方
			在第一行居中的方格内放1,依次向右上方填入2、3、4…,如果右上方已有数字,则向下移一格继续填写。
		2、loubere法生成奇阶幻方
			在居中的方格向上一格内放1,依次向右上方填入2、3、4…,如果右上方已有数字,则向上移两格继续填写
		3、horse法生成奇阶幻方
			(1)对于所有的奇阶幻方,在第一行居中的方格内放1,向左走1步,下走2步以跳马步,依次填入2、3、4…,
			若出到方阵下方,把该数字填到本该填数所在列上方相应的格;
			若出到方阵右方,把该数字填到本该填数所在行的左方相应的格;
			如果落步格已有数字, 则向下移一格继续填写。

			(2)n阶奇阶幻方,若n为不是3的倍数,那么在任意一格内放1,向左走1步,下走2步以跳马步,依次填入2、3、4…,
			若出到方阵下方,把该数字填到本该填数所在列上方相应的格;
			若出到方阵右方,把该数字填到本该填数所在行的左方相应的格;
			如果落步格已有数字, 则向上移一格继续填写
	二、当n为偶数时称为偶阶幻方;
		偶阶幻方分为双偶幻方和单偶幻方。
		当n可以被4整除时,我们称该偶阶幻方为双偶幻方,如8阶、12阶、16阶等;
		当n不可被4整除时,我们称该偶阶幻方为单偶幻方,如6阶、10阶、14阶等
		1、双偶数幻方
			(1)Spring法生成双偶幻方
			方法:顺序填数,以中心点对称互换数字。
			第一步,先令a(i,j)=(i-1)*n+j,
				即第一行从左到可分别填写1、2、3、……、n;
				即第二行从左到可分别填写n+1、n+2、n+3、……、2n;…………n^2
			第二步,进行对称交换。
		2、单偶数幻方
			当n为非4倍数的偶数(即4n+2形)时:首先把大方阵分解为4个奇数(2m+1阶)子方阵。
			按上述奇数阶幻方给分解的4个子方阵对应赋值
			上左子阵最小(i),下右子阵次小(i+v),下左子阵最大(i+3v),上右子阵次大(i+2v)
			即4个子方阵对应元素相差v,其中v=n*n/4
			四个子矩阵由小到大排列方式为 
			① ③
			④ ②
			然后作相应的元素交换:a(i,j)与a(i+k,j)在同一列做对应交换
			(j<t或j>n-t+2),a(t-1,0)与a(t+k-1,0);a(t-1,t-1)与a(t+k-1,t-1)两对元素交换
			其中k=n/2,t=(n+2)/4 上述交换使每行每列与两对角线上元素之和相等。
			
*/
#include <stdio.h>
#include <math.h>
int a[256][256];
int sum;
int check(int n);
void oddMagic(int n);
void doubleEvenMagic(int n);
void singleEvenMagic(int n);
int main()
{
	int i,j,k,n;
	scanf("%d",&n);
	sum = (n*(n*n+1))/2;
	if(n%2==1)//奇数幻方
	{
		oddMagic(n);
		k=n;
	}else if(n%4==2)//单偶数幻方
	{
		singleEvenMagic(n);
	}else if(n%4==0)//双偶数幻方
	{
		doubleEvenMagic(n);
	}

	if(check(n)==1)
	{
		for(i=0;i<n;i++)
		{
			for(j=0;j<n;j++)
				printf("%5d",a[i][j]);
			printf("\n");
		}
	}
	return 0;
}
int check(int n)
{
	int i,j,sum1=0,sum2=0;
	printf("Checking......................\n");
	/*
		测试每一行的和是否为sum
		a[0][0]+a[0][1]+a[0][2]+...第一行
	*/
	for(i=0;i<n;i++)
	{
		for(j=0;j<n;j++)
			sum1+=a[i][j];
		if(sum1!=sum)
			return 0;
		sum1=0;
	}
	/*
		测试每一列的和是否为sum
		a[0][0]+a[1][0]+a[2][0]+..第一列
	*/
	for(i=0;i<n;i++)
	{
		for(j=0;j<n;j++)
			sum1+=a[j][i];
		if(sum1!=sum)
			return 0;
		sum1=0;
	}
	/*
		测试对角线上的和是否为sum
		a[0][0]+a[1][1]+a[2][2]+...+a[n][n]从左上角到右下角的各
		a[0][n-1]+a[1][n-2]+a[2][n-3]+...+a[n-1][0]从右上角到左下角的和
	*/
	for(sum1=0,i=0;i<n;i++)
	{
		sum1+=a[i][i];
		sum2+=a[i][n-i-1];
	}
	if(sum1!=sum)
		return 0;
	if(sum2!=sum)
		return 0;
	else return 1;
}
void oddMagic(int n)
{
	int x=0,y,m;
	y=n/2;
	printf("ins..............................\n");
	for(m=1;m<=n*n;m++)
	{
		a[x][y]=m;
		if(m%n!=0)
		{
			//后面的每一个数存放的行比前一个数的行数减1,列数加1
			x--;
			y++;
			//如果超界要从另一面进来
			if(x<0)
				x+=n;
			if(y==n)
				y=n-y;
		}else
		{
			//如果右上角已经有数字了,则后一个数字在当前数字下
			x++;
			if(x==n)
				x=x-n;
		}
	}
}
void doubleEvenMagic(int n)
{
		int x=1,i,j,k;
		//从左到右,从上到下,赋初值1,2,3...n^2;
		for(i=0;i<n;i++)
			for(j=0;j<n;j++)
				a[i][j]=x++;
		/*
			将幻方等分成m*m个4阶幻方,将各4阶幻方中对角线上(或非对角线上)
			的方格内数字与n阶幻方内以中心点为对称点的对角数字进行交换。	
		*/
		for(i=0;i<n;i++)
			for(j=0;j<n;j++)
			{
				/*
					满足条件时,i=0或4的倍数,所以j=i,或者i-j的绝对值是4的倍数
					1、从左上角到右下角对角线的值,a[0][0],a[1][1]...a[n][n]
					2、以a[0][4]为起点,k循环时向右下角的四个数a[1][5],a[2][6],a[3][7]
					3、以a[4][0]为起点,k循环时向右下角的四个数a[5][1],a[6][2],a[7][3]
				*/
				if(i%4==0 && abs(i-j)%4==0)
					for(k=0;k<4;k++)
						a[i+k][j+k]=n*n-a[i+k][j+k]+1;
				else if(i%4==3 && (i+j)%4==3)//右上角到左下角的
					for(k=0;k<4;k++)
						a[i-k][j+k]=n*n-a[i-k][j+k]+1;
			}
}
void singleEvenMagic(int n)
{
	int k,i,j,p,t;
	k=n/2;
	oddMagic(k);
	/*
		先赋初值
		上左子阵最小(i),下右子阵次小(i+v),下左子阵最大(i+3v),上右子阵次大(i+2v)
		即4个子方阵对应元素相差v,其中v=n*n/4
	*/		
	for(i=0;i<k;i++)
		for(j=0;j<k;j++)
		{
			a[i][j+k]=a[i][j]+2*k*k;
			a[i+k][j]=a[i][j]+3*k*k;
			a[i+k][j+k]=a[i][j]+k*k;
		}
	t=(n-2)/4;
	for(i=0;i<k;i++)
		for(j=0;j<k;j++)
		{
			if((j<t)&&(i<t))
			{
				p=a[i][j];
				a[i][j]=a[i+k][j];
				a[i+k][j]=p;
			}
			if((j<t)&&(i>k-t-1))
			{
				p=a[i][j];
				a[i][j]=a[i+k][j];
				a[i+k][j]=p;
			}
			if((i>=t&&i<=k-t-1)&&(j>=t&&j<t*2))
			{
				p=a[i][j];
				a[i][j]=a[i+k][j];
				a[i+k][j]=p;
			}
			if(j>1&&j<=t)
			{
				p=a[i][j+k];
				a[i][j+k]=a[i+k][j+k];
				a[i+k][j+k]=p;
			}
		}
}

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值