完美洗牌问题:给定一个数组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。