今天又發現一個關於完美洗牌的算法。這個比較簡單一些,由 microsoft的Peiyush Jain提出。 算法 原論文: A Simple In-Place Algorithm for In-Shuffle. 編程 Peiyush Jain, Microsoft Corporation. 數組 July 2004 less 問題描述: spa 所謂完美洗牌算法便是把輸入爲: code a_1,a_2........a_n,b_1,b_2.........b_n的序列變爲 orm b_1,a_1,b_2,a_2.......b_n,a_n 這是in perfect shufle。相對應的還有out perfect shuffle。二者區別在於首尾元素位置變或不變。 ci perfect shuffle算法要求在O(n),時間內,O(1)空間內完成。 input perfect shuffle實質是一個置換。置換爲: it i -> 2*i mod (2*n+1) 因爲置換能夠分解爲一系列不相交的輪換之積。故若是能找出全部輪換的一個表明元則可很容易解決問題。 如 n=3時 輸入 1 2 3 A B C b => A 1 B 2 C 3所對應的輪換爲(1,2,4)(3,6,5) 選表明元爲1和3以及一個臨時變量T: 2->T,1->2 1 2 3 A B C -----------> _ 1 3 A B C 4->1,T->4 _ 1 3 A B C -----------> A 1 3 2 B C 6->T,3->6 A 1 3 2 B C -----------> A 1 _ 2 B 3 5->3,T->5 A 1 _ 2 B 3 -----------> A 1 B 2 C 3 置換完成 所以問題就轉換爲求置換的輪換分解中的表明元問題了。 文中巧妙的利用特定條件下每一個不相交的輪換可有3的不一樣冪次生成。
見論文:A Simple In-Place Algorithm for In-Shuffle. Peiyush Jain, Microsoft Corporation. 一、問題描述: 所謂完美洗牌算法便是把輸入爲: a_1,a_2........a_n,b_1,b_2.........b_n的序列變爲 b_1,a_1,b_2,a_2.......b_n,a_n 這是in perfect shufle。 perfect shuffle算法要求在O(n),時間內,O(1)空間內完成。
perfect shuffle實質是一個置換。置換爲: i -> 2*i mod (2*n+1) 因爲置換能夠分解爲一系列不相交的輪換之積。故若是能找出全部輪換的一個表明元則可很容易解決問題。 如 len=3時 輸入 1 2 3 A B C b => A 1 B 2 C 3所對應的輪換爲(1,2,4)(3,6,5) 選表明元爲1和3以及一個臨時變量T: 1 2 3 A B C -----------> 2->T,1->2 _ 1 3 A B C -----------> 4->1,T->4 A 1 3 2 B C -----------> 6->T,3->6 A 1 _ 2 B 3 -----------> 5->3,T->5 A 1 B 2 C 3 置換完成 所以問題就轉換爲求置換的輪換分解中的表明元問題了。 文中巧妙的利用特定條件下: 即(2的任意次冪)對(3的k次冪)取模,能獲得一個輪換環,而且環與環之間恰好互不相交 。 如咱們分析長度2*n=3^k-1的置換的輪換分解。 (若長度爲2*4=8,則8=3^2-1) 考慮某一包含3^s( 1 =< s < k )的輪換。不妨記3^s爲a_1,3^k記爲m。 則輪換裏的數分別爲: a_2 = 2* a_1 mod m a_3 = 2* a_2 mod m; a_4 = 2* a_3 mod m; 。。。。。 。。。 a_len = 2* a_len-1 mod m a_1 = 2* a_len mod m 則 a_1 ≡2^len * a_1 mod m; ( 最後一項中的a_len用倒數第二行乘2替代,以此類推........) , 依此,假設m=9,能夠肯定每一個輪換中,len的值, 如a_1=3^s=3^0=1,則1=2^len * 1 mod m,則 len=2; 如a_1=3^s=3^1=3,則3=2^len * 3 mod m,則 len=6 所以每一個3^s開始的一個輪換知足 : 3^s ≡3^s * 2^len mod 3^k ,且輪換的長度爲len。 補充:若對1 2 3 4 5 6 7 8 9 10進行洗牌則首先交換成 1 2 3 4 6 7 8 9 5 10 (n=4,所以咱們只對須要的前8個先調整到前面) 下面舉了個例子:洗牌置換過程例證,它例舉了將1234ABCD置換成A1B2C3D4的過程,請將圖中的n理解爲len,k=2。 |
#include "stdio.h" //輪換 void Cycle(int Data[],int Lenth,int Start) { int Cur_index,Temp1,Temp2; Cur_index=(Start*2)%(Lenth+1); Temp1=Data[Cur_index-1]; Data[Cur_index-1]=Data[Start-1]; while(Cur_index!=Start) { Temp2=Data[(Cur_index*2)%(Lenth+1)-1]; Data[(Cur_index*2)%(Lenth+1)-1]=Temp1; Temp1=Temp2; Cur_index=(Cur_index*2)%(Lenth+1); } } //數組循環移位 參考編程珠璣 void Reverse(int Data[],int Len) { int i,Temp; for(i=0;i<Len/2;i++) { Temp=Data[i]; Data[i]=Data[Len-i-1]; Data[Len-i-1]=Temp; } } void ShiftN(int Data[],int Len,int N) { Reverse(Data,Len-N); Reverse(&Data[Len-N],N); Reverse(Data,Len); } //知足Lenth=3^k-1的perfect shfulle的實現 void Perfect1(int Data[],int Lenth) { int i=1; if(Lenth==2) { i=Data[Lenth-1]; Data[Lenth-1]=Data[Lenth-2]; Data[Lenth-2]=i; return; } while(i<Lenth) { Cycle(Data,Lenth,i); i=i*3; } } //查找最接近N的3^k int LookUp(int N) { int i=3; while(i<=N+1) i*=3; if(i>3) i=i/3; return i; } void perfect(int Data[],int Lenth) { int i,startPos=0; while(startPos<Lenth) { i=LookUp(Lenth-startPos); ShiftN(&Data[startPos+(i-1)/2],(Lenth-startPos)/2,(i-1)/2); Perfect1(&Data[startPos],i-1); startPos+=(i-1); } } #define N 100 void main() { int data[N]={0}; int i=0; int n; printf("please input the number of data you wanna to test(should less than 100):/n"); scanf("%d",&n); if(n&1) { printf("sorry,the number should be even "); return; } for(i=0;i<n;i++) data[i]=i+1; perfect(data,n); for(i=0;i<n;i++) printf("%d ",data[i]); }