1. 程式人生 > >簡單易懂的Dancing links講解(1)

簡單易懂的Dancing links講解(1)

最早接觸Dancing Links的時候,是在csdn論壇上逛的時候,發現有人在研究數獨程式,由於本人開發過數獨遊戲,就進去看了看,發現有人說用Dancing Links來求解數獨最快了,於是我就決定去了解一下Dancing Links。

1.   Dancing Links是什麼?

Dancing Links是一類搜尋問題的通用優化,對精確覆蓋問題有奇效,也可解決重複覆蓋問題,DancingLinks的發明者是DonKnuth(《計算機程式設計藝術》的作者)

2.   用途

        解決精確覆蓋問題。對於接下來的非確定性演算法,由於我們沒有想到更好的名字,我們將稱之為X演算法,它能夠找到由特定的01矩陣A定義的精確覆蓋問題的所有解。X演算法是實現試驗——錯誤這一顯而易見的方法的一段簡單的語句(確實,一般來說,我想不到別的合理的方法來完成這個工作)。

        如果A是空的,問題解決;成功終止。                                 

        否則,選擇一個列c(確定的是選含1最少的列)。                                

        選擇一個行r,滿足 A[r,c]=1 (不確定的是哪一行)。                             

         把r包含進部分解。                              

  對於所有滿足 A[r,j]=1 的j,                               

  從矩陣A中刪除第j列;                               

  對於所有滿足 A[i,j]=1 的,                             

    從矩陣A中刪除第i行。                              

在不斷減少的矩陣A上遞迴地重複上述演算法。

3.   舞蹈步驟

       一個實現X演算法的好方法就是將矩陣A中的每個1用一個有5個域L[x]、R[x]、U[x]、D[x]、C[x]的資料物件(dataobject)x來表示。矩陣的每行都是一個經由域L和R(左和右)雙向連線的環狀連結串列;矩陣的每列是一個經由域U和D(上和下)雙向連線的環狀連結串列。每個列連結串列還包含一個特殊的資料物件,稱作它的表頭(list header)。

       這些表頭是一個稱作列物件(columnobject)的大型物件的一部分。每個列物件y包含一個普通資料物件的5個域L[y]、R[y]、U[y]、D[y]和C[y],外加兩個域S[y](大小)和N[y](名字);這裡大小是一個列中1的個數,而名字則是用來標識輸出答案的符號。每個資料物件的C域指向相應列頭的列物件。

       表頭的L和R連線著所有需要被覆蓋的列。這個環狀連結串列也包含一個特殊的列物件稱作根,h,它相當於所有活動表頭的主人。而且它不需要U[h]、D[h]、C[h]、S[h]和N[h]這幾個域。

       我們尋找所有精確覆蓋的不確定性演算法現在可以定型為下面這個明析、確定的形式,即一個遞迴過程search(k),它一開始被呼叫時k=0:

      如果 R[h]=h ,列印當前的解(見下)並且返回

否則選擇一個列物件c(見下)。               

覆蓋列c(見下)。                 

對於每個r←D[c],D[D[c]],……,當 r!=c,                

  設定 Ok<-r;              

  對於每個j←R[r],R[R[r]],……,當 j!=r,                

    覆蓋列j(見下);                

  search(k+1);              

 設定 r←Ok 且 c←C[r];                 

  對於每個j←L[r],L[L[r]],……,當 j!=r,                

    取消列j的覆蓋(見下)。                

取消列c的覆蓋(見下)並且返回。                

為了選擇一個列物件c,我們可以簡單地設定c<-R[h];這是最左邊沒有覆蓋的列。或者如果我們希望使分支因數達到最小,我們可以設定s<-無窮大,那麼接下來:                

對於每個j←R[h],R[R[h]],……,當 j!=h,                

  如果 S[j]<s 設定 c←j 且 s←S[h]。                

那麼c就是包含1的序數最小的列(如果不用這種方法減少分支的話,S域就沒什麼用了)。               

覆蓋列c的操作則更加有趣:把c從表頭刪除並且從其他列連結串列中去除c連結串列的所有行。                

設定 L[R[c]]←L[c] 且 R[L[c]]←R[c]。               

對於每個i←D[c],D[D[c]],……,當 i!=c,                

  對於每個j←R[i],R[R{i]],……,當 j!=i,                

    設定 U[D[j]]←U[j],D[U[j]]←D[j],               

    並且設定 S[C[j]]←S[C[j]]-1。              

操作(1),就是我在本文一開始提到的,在這裡他被用來除去水平、豎直方向上的資料物件。              

最後,我們到達了整個演算法的尖端,即還原給定的列c的操作。這裡就是連結串列舞蹈的過程:              

對於每個i←U[c],U[U[c]],……,當 j!=i,                

  對於每個j←L[i],L[L[i]],……,當 j!=i,                

    設定 S[C[j]]←S[C[j]]+1,              

    並且設定 U[D[j]]←j,D[U[j]]←j。                 

設定 L[R[c]]←c 且 R[L[c]]←c。              

          注意到還原操作正好與覆蓋操作執行的順序相反,我們利用操作(2)來取消操作(1)。(其實沒必要嚴格限制“後執行的先取消”,由於j可以以任何順序穿過第i行;但是從下往上取消對行的移除操作是非常重要的,因為我們是從上往下把這些行移除的。相似的,對於第r行從右往左取消列的移除操作也是十分重要的,因為我們是從左往右覆蓋的。)

       以上部分,可能太過專業,太抽象,你可能也看不明白,反正我就是一直沒弄明白過上面的描述,但是後面會有我自己通過分析程式程式碼給出的示意圖,詳細描述了舞蹈的過程,非常直觀易懂,所以不懂上面的可直接跳過,不必糾結。

4.   Dancing Links的具體實現方法

        Dancing Links這種資料結構的實現方法有兩種,一個是用雙向十字連結串列來實現,這種方法比較容易理解,另一個是用幾個陣列來實現,這種方法較另類,較難理解  

雙向十字連結串列實現

L[x]和R[x]分別表示x的前驅節點和後繼節點。每個程式設計師都知道如下操作:

L[R[x]] ← L[x],R[L[x]] ← R[x] (1)

是將x從連結串列刪除的操作;但是隻有少數程式設計師意識到如下操作

L[R[x]] ← x,R[L[x]] ← x (2)

dancingLinks的精髓就體現在第二個公式,因為它很方便很高效的實現了連結串列的恢復

圖1:dancingLinks的連結串列結構示意圖

dancing links的陣列實現

其實是用陣列來模擬雙向十字連結串列結構,為方便理解,直接舉個例子。

對於如下的01矩陣:

11 00

00 01

01 11

10 10

我們把它的dancinglinks結構描述到如下4個數組中:(Up,Down,Left,Right)


如下:


為什麼這4個數組的長度都是13呢?                   

      因為上面的01矩陣有4個列,8個1,我們把頭節點head編號為0。列分別編號為1,2,3,4。第一行的兩個1編號為5,6,第二行的一個1編號為7,第三行三個1編號為8,9,10。第四行兩個1,編號為11,12。編號的順序都是從左到右。   

      這樣一來,1列的下一個節點就是編號為5的1,編號為5的1的下面又是編號11的1,編號為5的1的左邊和右邊都是編號為6的1            

      為便於理解,可以結合圖1來看

Remove(5)  L[R[1]]=L[1];    R[L[1]]=R[1]; L[R[11]]=L[11]; R[L[11]]=R[11];

Remove(6)  L[R[8]]=L[8];    R[L[8]]=R[8]; L[R[2]]=L[2];    R[L[2]]=R[2];


Resume(5)  L[R[1]]=R[L[1]]=1    L[R[11]]=R[L[11]]=11

Resume(6)  L[R[2]]=R[L[2]]=2    L[R[8]]=R[L[8]]=8


可見陣列是可以用來表示雙向十字連結串列結構的