关于魔方矩阵的心得

来源:互联网 发布:js trim函数在哪里 编辑:程序博客网 时间:2024/05/16 05:01
    最近碰到了个关于魔方矩阵的问题,有些心得与大家分享。
    关于那些什么历史的东西这里就不在赘述了。魔方矩阵,顾名思义,是一个二维方阵,n行n列。并且每一行每一列的所有数字之和都是相等的。同时主副对角线的数字和也是相等的。
    关于如何构造魔方矩阵有许多方法,各种情况下又有不同的简洁方法。
    我先说一个笔者自己想出的一个通用的方法。首先,对于n*n个数,要排成行、列、对角线都相等的方阵,大多数人的想法是首先求和算一下每行的和是多少。但是,笔者用血的事实告诉你们这个方法不好。我们可以吧数字分分类,1、2、3....n-1、n,n+1、n+2、n+3....2*n,.....,(n-1)*n+1、......、n*n。有人已经知道我的想法了,以行为例,如果可以使每行有:1、2、3......n、0*n、1*n......(n-1)*n,那么必然和是相等的。那么这样的情况可以构造吗?答案是显然的,同时考虑到列,只要以平行于任意对角线的方向分布,每n个连续的数占一个“对角线”,但是只有对角线有n个,不过其他的可以两两组合构成n个。所以,我们选取一个方向依次排列便可达到行和列的和满足要求。同时在两组数交界处采取相同的方向移动。再来考虑对角线,与我们选取方向相同的对角线容易些,我们只要让n*n个数的中间n个数放在对角线就可以了,进一步分析,1开始的位置必须是中间或者中间之一。当n为奇数是1在中间即可,
    当n为偶数时,我开始的想法是这样。1在中间的两边其实都是一样的,毕竟是可以翻转的,不过考虑到对角线的相等,可易得对角线上的数字为所有数字中间的n个。那么按原来的方法显然不行,我们可以把数字的开始改变,1+n/2或者1-n/2+n*n。而且选取的平行方向也有关联,1+n/2选择朝中间的另一边。这样,一条对角线就满足了,同时可证另一条对角线自然满足,有对称性可证。但是逻辑强的人就会发现。我漏了一点,行列没有同时满足。所以是不可行的。具体可以自己写代码或者稿纸上模拟。偶数应为是偶数,每次行或列只能满足一个,另一个会一2个2个的跳跃式分布。好吧,只能按照网上的方法了。
    网上有许多其他的方法。
    百度中的平面魔方的构造
平面魔方的一般定义:将自然数 1 到 N^2, 排列 N 行 N 列的方阵,使每行、每列及两条主对角线上的 N 个数的和都等于 (N^2+1)/2,这样的方阵称为 N 阶幻方。
通过搜索整理后,得到下面的算法:
对平面魔方的构造,分为三种情况:N为奇数、N为4的倍数、N为其它偶数(4n+2的形式)
1、 N 为奇数时
(1) 将1放在第一行中间一列;
(2) 从2开始直到n×n止各数依次按下列规则存放:
按 45°方向行走,如向右上
每一个数存放的行比前一个数的行数减1,列数减1
(3) 如果行列范围超出矩阵范围,则回绕。
例如1在第1行,则2应放在最下一行,列数同样减1;
(4) 如果按上面规则确定的位置上已有数,或上一个数是第1行第n列时,
则把下一个数放在上一个数的下面。
这种方法和我想的是一样的。
2、N为4的倍数时
采用对称元素交换法。
首先把数1到n×n按从上至下,从左到右顺序填入矩阵
然后将方阵的所有4×4子方阵中的两对角线上的数关于大方阵中心作中心对称交换(注意是各各子矩阵对角线上面的数), 即a(i,j)与a(n+1-i,n+1-j)交换,所有其它位置上的数不变。(或者将对角线不变,其它位置对称交换也可)
这个方法很巧,虽然直接实现的难度较大,不过有一种替代的方法,c代码如下

void even_even(unsigned char rowcol){unsigned order1=rowcol*rowcol,order2=1;unsigned char row,col,k;for(row=0;row<rowcol;row++)for(col=0;col<rowcol;col++)if ((row%4)==(col%4)||((row%4)+(col%4)==3))       //关键操作{matrix[row][col]=order1--;order2++;}else{matrix[row][col]=order2++;order1--;}}

3、N 为其它偶数时
当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+u,j)在同一列做对应交换(j<t-1或j>n-t+1),
注意其中j可以去零。
a(t-1,0)与a(t+u-1,0);a(t-1,t-1)与a(t+u-1,t-1)两对元素交换
其中u=n/2,t=(n+2)/4 上述交换使每行每列与两对角线上元素之和相等。
思路如上,和高阶数独思路有些像,先分开,在整体做平衡处理。
奇数的实现#include <stdio.h>#include <math.h>int main(){int a[16][16],i,j,n,k;printf("Plase input n(1-15,it must be odd.): ");scanf("%d",&n);while(!(n>=1&&n<=15)||n%2==0){printf("The number is invalid.Plase insert again:");scanf("%d",&n);}for(i=1;i<=n;i++)for(j=1;j<=n;j++)a[i][j]=0;j=n/2+1;a[1][j]=1;i=1;for(k=2;k<=n*n;k++){i=i-1;j=j+1;if(i==0&&j==n+1){i=i+2;j=j-1;}else{if(i==0)i=n;if(j==n+1)j=1;}if(a[i][j]==0)a[i][j]=k;else{i=i+2;j=j-1;a[i][j]=k;}}for(i=1;i<=n;i++){for(j=1;j<=n;j++)printf("%5d",a[i][j]);printf("\n");}return 0;}所有的实现#include <stdio.h>#define N 100unsigned matrix[N][N];void init(void);void odd(unsigned startdigit,unsigned char startrow,unsigned char startcol,unsigned char rowcol);void odd_even(unsigned char rowcol);void even_even(unsigned char rowcol);void print(unsigned char rowcol);void verify(unsigned char rowcol);int main(){unsigned char number;printf("Please input a number:");scanf("%d",&number);init();if(number%2)odd(1,0,0,number);else if(number%4)odd_even(number);else even_even(number);print(number);verify(number);return 0;}void init(void){unsigned char i,j;for(i=0;i<N;i++)    for(j=0;j<N;j++)         matrix[i][j]=0;}void odd(unsigned startdigit,unsigned char startrow,unsigned char startcol,unsigned char rowcol){unsigned char row,col,row0,col0;unsigned i;row0=row=startrow;col=(rowcol-1)/2+startcol;matrix[row][col]=startdigit;for(i=startdigit+1;i<=startdigit+rowcol*rowcol-1;i++){if(row-1<startrow)row=startrow+rowcol-1;else row--;if (col+1>startcol+rowcol-1)col=startcol;else col++;if (!matrix[row][col]){row0=row;col0=col;matrix[row][col]=i;}else{row=row0;if (row+1>startrow+rowcol-1)row=startrow;else row++;col=col0;matrix[row][col]=i;}}}void odd_even(unsigned char rowcol){unsigned char k=rowcol/2;unsigned char row,col;unsigned t,l;odd(1,0,0,k);odd(k*k+1,k,k,k);odd(2*k*k+1,0,k,k);odd(3*k*k+1,k,0,k);for(col=0;col<=k/2-1;col++)for(row=0;row<k;row++)if(!(col==0&&row+k==k/2+k)){t=matrix[row+k][col];matrix[row+k][col]=matrix[row][col];matrix[row][col]=t;}t=matrix[(k-1)/2+k][(k-1)/2];matrix[(k-1)/2+k][(k-1)/2]=matrix[(k-1)/2][(k-1)/2];matrix[(k-1)/2][(k-1)/2]=t;l=(k-1)/2;for(col=1;col<l-1;col++)for (row=k;row<rowcol;row++){t=matrix[row][rowcol-col-1];matrix[row][rowcol-col-1]=matrix[row-k][rowcol-col-1];matrix[row-k][rowcol-col-1]=t;}}void even_even(unsigned char rowcol){unsigned order1=rowcol*rowcol,order2=1;unsigned char row,col,k;for(row=0;row<rowcol;row++)for(col=0;col<rowcol;col++)if ((row%4)==(col%4)||((row%4)+(col%4)==3)){matrix[row][col]=order1--;order2++;}else{matrix[row][col]=order2++;order1--;}}void print(unsigned char rowcol){char *str="\xc4\xc4\xc4\xc4";unsigned char i,j;for(i=0;i<rowcol;i++){for(j=0;j<rowcol;j++){if(i==0)if(j==0)printf("\xda");    else printf("\xc2");elseif(j==0)printf("\xc3");    else printf("\xc5");printf("%s",str);}if(i==0)printf("\xbf\n");else printf("\xb4\n");for(j=0;j<rowcol;j++)printf("\xb3%4d\n",matrix[i][j]);printf("\xb3\n");}printf("\xc0%s",str);for(j=1;j<rowcol;j++)printf("\xc1%s",str);printf("\xd9\n");}void verify(unsigned char rowcol){unsigned char i,j;unsigned sum=0,sum1=0;for(i=0;i<rowcol;i++){sum1=0;for(j=0;j<rowcol;j++)sum1+=matrix[i][j];if (sum==0)sum=sum1;else if(sum1!=sum){printf("Error!\n");return;}}for(i=0;i<rowcol;i++){sum1=0;for(i=0;i<rowcol;j++)sum1+=matrix[j][i];if(sum==0)sum=sum1;else if(sum1!=sum){printf("Error!\n");return;}    }    printf("Verify OK! The sum is %d\n",sum);/*sum=n*n*(n*n+1)/2*/}


原创粉丝点击