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