1. 程式人生 > >perfect shuffle 完美洗牌演算法

perfect shuffle 完美洗牌演算法

完美洗牌問題:給定一個數組a1,a2,a3,...an,b1,b2,b3..bn,把它最終設定為b1,a1,b2,a2,...bn,an這樣的。

  perfect shuffle實質是一個置換。置換為: ­

                  i -> 2*i mod (2*n+1) ­

置換可以分解為一系列不相交的輪換之積。故如果能找出所有輪換的一個代表元則可很容易解決問題。 ­我們的關鍵問題是從每個圈裡選擇任意一個位置作代表,每個圈從這個位置位置走一圈。

結論:對於2 * n = (3^k - 1),這種長度的陣列,恰好只有k個圈,並且每個圈的頭部是1,3,9,...3^(k - 1)。

對於一般的n,把它拆成兩部分,前一部分是滿足結論,後一部分再單獨算。為了把陣列分成適當的兩部分,我們同樣需要交換一些元素。假設滿足結論的需要的長度是2 * m = (3^k - 1), 我們需要把n分解成m和n - m兩部分,按下標來說,是這樣:

原先的陣列(1..m) (m + 1.. n) (n + 1..n + m)(n + m + 1..2 * n),我們要達到的陣列 (1..m)(n + 1.. n + m)(m + 1..n)(n + m + 1..2  * n)。可見,中間那兩段長度為(n - m)和m的段需要交換位置,這個相當於把(m + 1..n + m)的段迴圈右移m次。

演算法步驟:

輸入陣列 a[1..2 * n]

step 1 找到 2 * m = 3^k - 1 使得 3^k <= 2 * n < 3^(k +1)

step 2 把a[m + 1..n + m]那部分迴圈移m位

step 3 對每個i = 0,1,2..k - 1,3^i是個圈的頭部,做cycle_leader演算法,陣列長度為m,所以對2 * m + 1取模。

step 4 對陣列的後面部分a[2 * m + 1.. 2 * n]繼續使用本演算法,這相當於n減小了m。