一、认识幻方
说到幻方,很多人可能还不知道什么是幻方。幻方是一种将一些连续的正整数安排在正方形格子中,使每行、列和对角线上的数字和都相等的方法。因为正方形格子可能是3*3或者n*n的,所以幻方就有了几阶幻方的说法。需要注意的是幻方最低是3阶!
三阶幻方 四阶幻方
二、n阶幻方实现
在网上参考了现有的求解幻方的算法,将幻方进行了分类:奇数阶幻方、单偶阶幻方、双偶阶幻方。接下来使用java来实现这三种幻方的求法。
奇数阶幻方
一、 奇数阶幻方我采用了最简单的罗伯法:
1、将最小的数放在第一行最中间
2、然后依次将下一个数放在上一个数的右上方
3、如果下一个数超出了上边界则放在下边界,纵坐标不变;超出了右边界则放在左边界,横坐标不变
4、如果下一个数的位置已经存放了数,则将下一个数放在上一个数的下方
二、思考如何将算法转换成代码
1、如何存放幻方(即如何将存放数字的排列)
数组作为一种常用的存储数据的引用类型,其下标还可以很好的方便我们对幻方里的值进行查询和修改,所以使用int类型的二维数组存储幻方中的数字。
int[][] arr=new int[len][len];//len是幻方的阶数
2、如何记录上一个数与当前数的坐标
arr数组的下标即可以记录数字的下标。我们将一系列连续的数存放在数组中肯定会使用到for循环,那么完全可以使用两个int值x,y来记录上个数的下标,然后根据罗伯法对x,y的值进行修改得到正确的当前数的下标
三、代码填充
1、定义二位数组,长度为幻方阶数
int[][] arr=new int[len][len];//len是幻方的阶数
2、将最小的数num存放在第一行正中间,同时记录该数的坐标
arr[0][len/2]=num;
int x=0;int y=len/2;
3、使用for循环存放数字
for(int i=num+1;i<len*len+num;i++){}
ps:接下来的代码都将在这个for循环里面
4、确定当前数的坐标
理想状态下当前数的坐标是x-1,y+1。如果x-1越界,则直接将x赋值为len-1即下边界;同理,如果y+1越界则直接将y赋值为0即左边界。
如果当前数的坐标已经存放了数字,则找到上一个数的坐标(大家可以自己写出二位数组中查询某个数下标的方法,此处使用getIndex(int[][] arr,int num这个方法来表示))然后行数加1,注意此处仍然需要注意x是否越界。同时此处得到的新的坐标也有可能存在数字,所以可以放在while循环里。
if(x-1<0){
x=arr.length-1;//x越界
}else{
x=x-1;
}
if(y+1>arr.length-1){
y=0;//y越界
}else{
y=y+1;
}
while(true){
if(arr[x][y]==0){//当前坐标数组没有存放数字,直接存放
arr[x][y]=i;
break;
}else{//当前坐标数组存放数字,需要获取上个数的坐标
int[] js = getIndex(arr, i-1);
x=js[0]+1;//使当前数的坐标变成上个数的下面的坐标
y=js[1];
if(x>arr.length-1){
x=0;
}
}
}
四、代码测试
经测试可以看到,代码可以正确实现奇数阶幻方
双偶阶幻方
一、双偶阶幻方我使用了对称交换法:
二、代码实现
for(int i=0;i<len;i++){
for(int j=0;j<len;j++){
arr[i][j]=a;
a++;
}
}
int num=arr[i][i];
arr[i][i]=arr[len-1-i][len-1-i];
arr[len-1-i][len-1-i]=num;
num=arr[i][len-1-i];
arr[i][len-1-i]=arr[len-1-i][i];
arr[len-1-i][i]=num;
}
三、代码测试
单偶阶幻方
一、单偶阶幻方使用了象限对称交换法
二、填充代码
int[][] is2 = getSingleArray(len/2,1+len*len/4);
int[][] is3 = getSingleArray(len/2,1+2*len*len/4);
int[][] is4 = getSingleArray(len/2,1+3*len*len/4);
for(int j=0;j<len;j++){
if(i<len/2&&j<len/2){//左上角
arr[i][j]=is1[i][j];
}else if(i>=len/2&&j>=len/2){//右下角
arr[i][j]=is2[i-len/2][j-len/2];
}else if(i<len/2&&j>=len/2){//右上角
arr[i][j]=is3[i][j-len/2];
}else{//左下角
arr[i][j]=is4[i-len/2][j];
}
}
}
if(i==len/4){
for(int j=0;j<len/4;j++){//左上角正中间开始向右len/4个与左下角对应的坐标交换值
int s=arr[i][i+j];
arr[i][i+j]=arr[i+len/2][i+j];
arr[i+len/2][i+j]=s;
}
for(int j=0;j<len/4;j++){
int s=arr[i][j];
arr[i][j]=arr[i+len/2][j];
arr[i+len/2][j]=s;
}
}
if(len/4-1!=0){
for(int j=len/2+len/4;j<len-2;j++){//右上角中间一列及其右侧len/4-2列与右下角对应的坐标交换值
for(int k=0;k<len/2;k++){
int s=arr[k][j];
arr[k][j]=arr[k+len/2][j];
arr[k+len/2][j]=s;
}
}
}
}
}