完美洗牌算法

Posted Jarvis_Xu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了完美洗牌算法相关的知识,希望对你有一定的参考价值。

完美洗牌算法

题目详情:有个长度为2n的数组{a1,a2,a3,...,an,b1,b2,b3,...,bn},希望排序后{a1,b1,a2,b2,....,an,bn},请考虑有无时间复杂度o(n),空间复杂度0(1)的解法。
http://blog.csdn.net/starcuan/article/details/19079913
#include <iostream>       
#include <algorithm>  
#include <functional>  
using namespace std;      
/* 
    完美洗牌算法 
    题目详情:有个长度为2n的数组{a1,a2,a3,...,an,b1,b2,b3,...,bn}, 
    希望排序后{a1,b1,a2,b2,....,an,bn}, 
    请考虑有无时间复杂度o(n),空间复杂度0(1)的解法。 
*/  
#define N 1000  
  
// 时间O(n),空间O(n) 数组下标从1开始    
//任意的第i个元素,最终换到了 (2 * i) % (2 * n + 1)的位置  
void pefect_shuffle1(int *a,int n) {    
    int n2 = n * 2, i, b[N];    
    for (i = 1; i <= n2; ++i) {    
        b[(i * 2) % (n2 + 1)] = a[i];    
    }    
    for (i = 1; i <= n2; ++i) {    
        a[i] = b[i];    
    }    
}    
//时间O(nlogn) 空间O(1) 数组下标从1开始      
void perfect_shuffle2(int *a,int n) {      
    int t,i;      
    if (n == 1) {      
        t = a[1];      
        a[1] = a[2];      
        a[2] = t;      
        return;      
    }      
    int n2 = n * 2, n3 = n / 2;      
    if (n % 2 == 1) {  //奇数的处理      
        t = a[n];      
        for (i = n + 1; i <= n2; ++i) {      
            a[i - 1] = a[i];      
        }      
        a[n2] = t;      
        --n;      
    }      
    //到此n是偶数      
          
    for (i = n3 + 1; i <= n; ++i) {      
        t = a[i];      
        a[i] = a[i + n3];      
        a[i + n3] = t;      
    }      
          
    // [1.. n /2]      
    perfect_shuffle2(a, n3);      
    perfect_shuffle2(a + n, n3);      
}      
/* 
  
    第一个圈:1 -> 2 -> 4 -> 8 -> 7 -> 5 -> 1 
    第二个圈:3 -> 6 -> 3: 
 
    原始数组:1 2 3 4 5 6 7 8 
    数组小标:1 2 3 4 5 6 7 8 
 
    走第一圈:5 1 (3) 2 7 (6) 8 4 
    走第二圈:5 1 6 2 7 3 8 4  
*/  
/* 
神级结论:若2*n=(3^k - 1),则可确定圈的个数及各自头部的起始位置 
对于2*n = (3^k-1)这种长度的数组,恰好只有k个圈,且每个圈头部的起始位置分别是1,3,9,...3^(k-1)。 
*/  
//数组下标从1开始,from是圈的头部,mod是要取模的数 mod 应该为 2 * n + 1,时间复杂度O(圈长)   
void cycle_leader(int *a,int from, int mod) {    
    int last = a[from],t,i;     
    for (i = from * 2 % mod;i != from; i = i * 2 % mod) {    
        t = a[i];    
        a[i] = last;    
        last = t;       
    }    
    a[from] = last;    
}    
//翻转字符串时间复杂度O(to - from)    
void reverse(int *a,int from,int to) {    
int t;    
    for (; from < to; ++from, --to) {    
        t = a[from];    
        a[from] = a[to];    
        a[to] = t;    
    }    
        
}    
//循环右移num位 时间复杂度O(n)    
void right_rotate(int *a,int num,int n) {    
    reverse(a, 1, n - num);    
    reverse(a, n - num + 1,n);    
    reverse(a, 1, n);    
}    
/* 
完美洗牌算法,其算法流程为: 
    输入数组 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。 
*/  
//时间O(n),空间O(1)    
void perfect_shuffle3(int *a,int n) {    
    int n2, m, i, k,t;    
    for (;n > 1;) {    
        // step 1    
        n2 = n * 2;    
        for (k = 0, m = 1; n2 / m >= 3; ++k, m *= 3)  ;    
        m /= 2;    
        // 2m = 3^k - 1 , 3^k <= 2n < 3^(k + 1)    
            
        // step 2    
        right_rotate(a + m, m, n);    
            
        // step 3    
            
        for (i = 0, t = 1; i < k; ++i, t *= 3) {    
            cycle_leader(a , t, m * 2 + 1);    
                
        }    
            
        //step 4    
        a += m * 2;    
        n -= m;    
    }    
    // n = 1    
    t = a[1];    
    a[1] = a[2];    
    a[2] = t;    
}    
int main()  
{  
    int arr[]={0,1,2,3,4,5,6,7,8};  
    perfect_shuffle3(arr,4);  
    for_each(arr,arr+9,[](int i){cout<<i<<endl;});  
    return 0;  
} 

  

以上是关于完美洗牌算法的主要内容,如果未能解决你的问题,请参考以下文章

完美洗牌算法

完美洗牌算法

算法系列:完美洗牌算法

完美洗牌算法

[转]完美洗牌(Perfect Shuffle)问题

游戏常用算法-洗牌算法