老师布置了一道c语言的题目:生成任意阶的魔方阵,通过阅读现有算法,作了自我实现,具体算法介绍可以参照:
http://www.cnblogs.com/codingmylife/archive/2010/12/24/1915728.html 这个网址,也可参照百度百科,非常全面。
魔方阵,古代又称“纵横图”,是指组成元素为自然数1、2…n的平方的n×n的方阵,其中每个元素值都不相等,且每行、每 列以及主、副对角线上各n个元素之和都相等。以下是代码,算法嵌入到注释中:
http://www.cnblogs.com/codingmylife/archive/2010/12/24/1915728.html 这个网址,也可参照百度百科,非常全面。
魔方阵,古代又称“纵横图”,是指组成元素为自然数1、2…n的平方的n×n的方阵,其中每个元素值都不相等,且每行、每 列以及主、副对角线上各n个元素之和都相等。以下是代码,算法嵌入到注释中:
/*
reference from
http://www.cnblogs.com/codingmylife/archive/2010/12/24/1915728.html
*/
#include<stdio.h>
/*
输出阵列
*/
int printA(int **magic,int n)
{ int i,j;
if(magic==NULL)
{
printf("参数为空指针\n");
return 0;
}
printf("输出结果:\n");
for(i=1;i<=n;i++)
{
//printf("......:\n");
for(j=1;j<=n;j++)
printf("%d\t", *(*(magic+i)+j));
printf("\n");
}
return 1;
}
/*
奇阶阵求法
奇数阶幻方最经典的填法是罗伯法。填写的方法是:
把1(或最小的数)放在第一行正中; 按以下规律排列剩下的(n×n-1)个数:
1、每一个数放在前一个数的右上一格;
2、如果这个数所要放的格已经超出了顶行那么就把它放在底行,仍然要放在右一列;
3、如果这个数所要放的格已经超出了最右列那么就把它放在最左列,仍然要放在上一行;
4、如果这个数所要放的格已经超出了顶行且超出了最右列,那么就把它放在前一个数的下一
行同一列的格内;
5、如果这个数所要放的格已经有数填入,那么就把它放在前一个数的下一行同一列的格内。
*/
void Odd(int n)
{
//声明填充计数next ,二维矩阵magic
int i,j;
int **magic;
//检查是否奇幻方
if((n/2)*2==n)
{
printf("Wrong method use!!\n");
return;
}
else
{
/*奇阶魔方阵生成算法*/
magic = new int *[n+1];
for(i=1;i<=n;i++)
{
magic[i]=new int [n+1];
for(j=1;j<=n;j++)
magic[i][j]=0;
}
//write
int fullFill(int **magic,int n,int next);
fullFill(magic, n ,1);
}//else
printA(magic,n);
}//endof Cardinal
/*
int fullFill()阶梯法填数组
***
*/
int fullFill(int **magic,int n,int next)
{
int nx,ny;
int end=n*n+next-1;
nx=1;
ny=(n+1)/2;
*(*(magic+nx)+ny)= next;
while(next<end)
{
nx--;
ny++;
next++;
if(nx==0&&ny!=(n+1))
nx=n;
else if(nx!=0&&ny==(n+1))
ny=1;
else if(nx==0&&ny==(n+1)|| *(*(magic+nx)+ny)!=0)
{
nx=nx+2;
ny--;
}
*(*(magic+nx)+ny)=next;
}//while
return 1;
}
/* --------
双偶阶阵求法void doubleEven()
对于n=4k阶幻方,我们先把数字按顺序填写。写好后,按4×4把它划分成k×k个方阵。
因为n是4的倍数,一定能用4×4的小方阵分割。
然后把每个小方阵的对角线,象制作4阶幻方的方法一样,对角线上的数字换成互补的数字,
就构成幻方
*/
void doubleEven(int n)
{
int i,j,m,q;
int next = 0;
int **magic;
if((n/4)*4!=n)
printf("Wrong method use!!\n");
else
{
magic=new int *[n+1];
for(i=1;i<=n;i++)
{
magic[i]=new int[n+1];
for(j=1;j<=n;j++)
*(*(magic+i)+j)=(++next);
}
/* 4阶小方阵对角线求补 */
for(m=1;m<=n;m+=4)
for(q=1;q<=n;q+=4)
{
//@对角线1上元素求补
i=m;
j=q;
while(i<=m+3&&j<=q+3)
{
*(*(magic+i)+j)=n*n+1-*(*(magic+i)+j);
i++;j++;
}
//@对角线2上的元素求补
i=m;
j=q+3;
while(i<=m+3&&j>=q)
{
*(*(magic+i)+j)=n*n+1-*(*(magic+i)+j);
i++;j--;
}
}//for for end
}//else end
printA(magic,n);
}
/*
单阶偶阵void singleEven()
(1)把方阵分为A,B,C,D四个象限,这样每一个象限肯定是奇数阶。用罗伯法,依次在A
象限,D象限,B象限,C象限按奇数阶幻方的填法填数。
(2)在A象限的中间行、中间格开始,按自左向右的方向,标出k格。A象限的其它行则标出
最左边的k格。将这些格,和C象限相对位置上的数,互换位置。
(3)在B象限任一行的中间格,自右向左,标出k-1列。(注:6阶幻方由于k-1=0,所以不用
再作B、D象限的数据交换),
将B象限标出的这些数,和D象限相对位置上的数进行交换,就形成幻方。
*/
void singleEven(int n)
{
int k,i,j,m,x,next=1,temp;
int **A;
int **B;
int **C;
int **D;
// @ printf("初始化二级指针");
A= new int *[n+1];
B= new int *[n+1];
C= new int *[n+1];
D= new int *[n+1];
for(i=1;i<=n;i++)
{
A[i] = new int[n+1];
B[i] = new int[n+1];
C[i] = new int[n+1];
D[i] = new int[n+1];
for(j=1;j<=n;j++)
{
A[i][j]=0;
B[i][j]=0;
C[i][j]=0;
D[i][j]=0;
}
}
if(n%4!=0&&n%2==0)
{
k=n/2; //每个小方阵的阶 n=2*k=2*(2m+1)=4m+2
m=(k-1)/2;//要向左或向右标记的元素个数k=2*m+1
x=k/2+1; //每个小方阵的中间行中间列
/*@初始化
四个象限的魔方阵*/
fullFill(A,k,1);
fullFill(D,k,k*k+1);
fullFill(B,k,2*k*k+1);
fullFill(C,k,3*k*k+1);
/*@交换元素**
*/
for(i=1;i<=m;i++)
{
//对中间行,包括中间元素向右标记m个元素,并交换A C对应位置
temp=C[x][x+i-1];
C[x][x+i-1]=A[x][x+i-1];
A[x][x+i-1]=temp;
//除中间行外,其他行从中间格开始,向左标记m个元素(不包含中间),并交换A C
中的相应位置
for(j=1;j<=k;j++)
if(j!=x)
{
temp=C[j][x-i];
C[j][x-i]=A[j][x-i];
A[j][x-i]=temp;
}
//从每一行的中间格开始向右标记m-1列,并交换B D对应位置
if(i<=m-1)
for(j=1;j<=k;j++)
{
temp=B[j][x+i-1];
B[j][x+i-1]=D[j][x+i-1];
D[j][x+i-1]=temp;
}
}
/*
printA(A,k);
printA(B,k);
printA(C,k);
printA(D,k);
*/
//@输出结果----------------------
printf("输出结果:\n");
for(i=1;i<=k;i++)
{
for(j=1;j<=k;j++)
printf("%d ",A[i][j]);
for(j=1;j<=k;j++)
printf("%d ",B[i][j]);
printf("\n");
}
for(i=1;i<=k;i++)
{
for(j=1;j<=k;j++)
printf("%d ",C[i][j]);
for(j=1;j<=k;j++)
printf("%d ",D[i][j]);
printf("\n");
}
//end output---------------------
}else
printf("Wrong method use!!\n");
}
//--->>>>>程序入口<<<<<----
int main()
{
int n;
for(;;)
{
printf("输入魔方阵阶数,回车结束:n=\n");
scanf("%d",&n);
if(n%2!=0)
Odd(n);
if(n%4==0)
doubleEven(n);
if(n%2==0&&n%4!=0)
singleEven(n);
printf("是否继续(0退出/1继续)?");
scanf("%d",&n);
if(n==0)
return 0;
}
return 0;
}