自編的一個魔方(數陣)算法,效率很高

      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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章