自编的一个魔方(数阵)算法,效率很高

来源:互联网 发布:安卓pdf阅读器源码 编辑:程序博客网 时间:2024/05/20 18:51

      N*N数阵就是N*N魔方,要求每行每列及对角线之和都相等。下面是大学时用TC写的一个算法,效率很高,找到所有4阶需要16秒,刚才增加三行代码消除对称的魔方,只需2秒即可找到所有的832种可能(不输出结果,只计数)。暂时还不知道标准答案,可能有遗漏或重复。

 

------------------------------------------------------------------------------------------------------------------------------------------------

 

    求解N阶魔方,即将1、2、3…N*N放入N*N的矩阵中,使得每行每列及两条对角线上的N个数的和都相等(等于SUM=(N*N)*(1+N*N)/(2*N))。

 

主要算法及思想:
    首先得到所有的N个数的和为SUM的组合情况,放在数组All[M][N]中,其中每一组中的N个数都是从小到大排列,M为所有的组的个数。Num[N*N]中存放了包含数字1、2…N*N的组的个数。Index[M*N]中存放了包含1、2…N*N的组的序号(以此序号可在All中找到对应的N个数),其中单元1-Num[1]中存放的是包含数字1的所有组的序号,单元Num[1]+1-Num[2]中存放了包含数字2的所有组的序号……。为方便得到包含任一个数字n的所有的组的序号,构造一存放查找偏移量的数组Offset[N*N],其中存放的是在Index数组中查找包含任一数字的组的序号时的起始偏移量,如:要找包含数字n的所有组,可以从Index数组中Offset[n]-Offset[n]+Num[n]单元得到组的序号,以此序号再到数组All中得到组中的N个数字。
    由于每一个组中的数字都是从小到大排列的,要得到同样N个数字的其他排列方式,可以通过函数Rotat得到(N个数共有N!种排列方式)。
    Matrix[N][N]中存放的是N*N阶矩阵的每一个元素。得到魔方的步骤如下:
1。取一组的N个数字,按一种排列方式,放入矩阵的第一行。
2。取一个包含第一行第一列中数字的组,按一种排列方式,放入矩阵的第一列。
3。取一个包含第一行第二列中数字的组,按一种排列方式,放入矩阵的第二列:
   每一列列中的每一个数字(除第一行的),都要与同一行的已放置的数字可以在同一个组中,如果是两条对角线上的数字,还要考虑与已放好的对角线上的数字能存在于同一个组中。如果不能满足这个条件,则更换这一列的N的数字的排列方式,如果所有的排列方式都不行,则另选一组放入此列中,若所有的组的任一种排列方式都不能放入此列,则将前一列的N个数字换一个排列方式,以此类推。
    通过函数CheckG(int group,int col)判断第group组能否放入第col列中;通过函数CheckR(int col)判断将某一组按某一方式放入第col列后,已有的所有元素能否同时存在。

   从以上的分析知,必须记录第一行和每一列中放置的组的序号和排列方式,以便更改。这些信息存放在数组States[N+1][3]中,其中第一维表示第一行和N列,第二维的3个单元分别存放:组的序号,排列方式,在包含某一数字的所有组中的位置。
   以下为N=3,M=8(SUM=15)时的一个实例分析:
All[8][3]:
 0:1 5 9(该组的序号为0,用0表示该组)
 1:1 6 8(该组的序号为1,用1表示该组)
 2:2 4 9
 3:2 5 8
 4:2 6 7
 5:3 4 8
 6:3 5 7
 7:4 5 6
    三个数字的和为15的组合只有以上8中。
所有组中包含数字1的组有(用组的序号表示):0 1;包含数字2的组有:2 3 4;包含数字3的组有:5 6;……
Num[9]:   2     3     2     3       4       3     2     3     2
Index[24]:
 (0 1)(2 3 4)(5 6)(2 5 7)(0 3 6 7)(1 4 7)(4 6)(1 3 5)(0 2)
    (0 1)为包含数字1的组;(2 3 4)为包含数字2的组;(5 6)为包含数字3的组;……
Offset[9]:0   2      5    7      10       14     17   19     22
    如要找包含数字5的组,可在Index中的10-10+4单元中找到组的序号0、3、6、7
                (10=Offset[5-1];4=Num[5-1])
寻找魔方的步骤:
1。将第一组按第一种排列方式放入第一行,States[0][0]=States[0][1]=0;
 1 5 9
 0 0 0
 0 0 0 (0表示还未放入数字)
2。考虑放入第一列:
 找到包含数字1的所有组中的第一个组(0:1 5 9),即States[1][2]=0;States[1][0]=0;
 由于此组已经用于放在第一行中,不符合条件。
 找到包含数字1的所有组中的第二个组(1:1 6 8),即States[1][2]++;States[1][0]=1;
 此组符合条件,按第一种排列方式将6 8放入第一列States[1][1]=0;
 1 5 9
 6 0 0
 8 0 0
 由于副对角线上的8和9不能同时出现在一个组中,不符合条件
 按第二种排列方式将6 8放入第一列States[1][1]++;
 1 5 9
 8 0 0
 6 0 0
 副对角线上的6 9也不能同时出现在一组,不符合条件
 由于没有其他的排列方式,只能另选一组
 包含数字1的组都不能满足条件,故考虑更换第一行
3。将第一行中的N个数换一种排列方式:States[0][1]++;
 1 9 5
 0 0 0
 0 0 0
4。考虑放入第一列:
 States[1][2]=0;States[1][0]=0;此组已放入第一行
 States[1][2]++;States[1][0]=1;States[1][1]=0;
 1 9 5
 6 0 0
 8 0 0
5。考虑放入第二列:
 找到包含数字9的所有组中的第一个组(0:1 5 9),即States[2][2]=0;States[2][0]=0;
 由于此组已用于第一行,不符合条件。
 找到包含数字9的所有组中的第二个组(2:2 4 9),即States[2][2]++;States[2][0]=2;
 按第一种排列方式States[2][1]=0;
 1 9 5
 6 2 0
 8 4 0
 由于主对角线上的1 2不能同时存在于一组中,不符合条件
 States[2][1]++;
 1 9 5
 6 4 0
 8 2 0
 两条对角线均不符合条件
 包含数字9的组均不符合条件,考虑更换第一列
 ……

最终找到一个魔方:
 6 1 8
 7 5 3
 2 9 4

原创粉丝点击