數組元素交叉排列的演算法題(a1 a2 a3 .. an b1 b2 b3 .. bn –>a 1 b1, a2 b2, a3 b3, .. an bn ) 概論思想(perfect

來源:互聯網
上載者:User
perfect shuffle 演算法

今天又發現一個關於完美洗牌的演算法。這個比較簡單一些,由 microsoft的Peiyush Jain提出。

原論文:      A Simple In-Place Algorithm for In-Shuffle.

                 Peiyush Jain, Microsoft Corporation.

                            July 2004

問題描述:

所謂完美洗牌演算法即是把輸入為:

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。相對應的還有out perfect shuffle。兩者區別在於首尾元素位置變或不變。

perfect shuffle演算法要求在O(n),時間內,O(1)空間內完成。

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

                                       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  ----------->  

                       4->1,T->4

_  1  3   A   B   C  ----------->  

                       6->T,3->6

A  1  3   2   B   C  ----------->

                       5->3,T->5

A  1  _   2   B   3  ----------->

A  1  B   2   C   3   置換完成

因此問題就轉換為求置換的輪換分解中的代表元問題了。

文中巧妙的利用特定條件下每個不相交的輪換可有3的不同冪次產生。

 

我們分析長度2*n=3^k-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_n = 2* a_n-1 mod m

  a_1 = 2* a_n   mod m

則    a_1  ≡2^n * a_1 mod m;  ( 最後一項中的a_n用倒數第二行乘2替代,以此類推........)

因此每個3^s開始的一個輪換滿足 :  3^s ≡3^s * 2^n mod 3^k ,且長度為n

現假設兩個不同的3^s開始的輪換存在相交的元素,記為:p

p≡3^i*2^n mod 3^s

p≡3^j*2^m mod 3^s  (i,j<s)

若n,m都為0,則顯然i=j; 假設 i>j

否則應有:3^s |(3^i*2^n -3^j*2^m) ===>3^s |{ 3^i*( 2^n-3^(j-i)*2^m ) }

因為: gcd(3^s , ( 2^n -3^(j-i)*2^m) )=1   注:2^n - 3^(j-i)*2^m 只含2的冪次因子.因為由初等的數論知識可知道

am+bn即m,n的線性組合只能表示gcd(m,n)的倍數.

 

因此上面等式不能成立.

因此每個以不同的3^i開始的輪換不會相交.

上面證明了每個3^i開始的輪換不相交,還需要計算每個3^i起始的輪換覆蓋了所有的元素,這可以採用計數的方法證明.

因為每個3^i開始的輪換的長度滿足:

                                                  3^i≡3^i *2^N mod3^s 即2^N≡1mod3^(s-i) 

           所以N=φ(3^(s-i))=φ(3^s)/3^i                                 {  gcd (2,3)=1, N是滿足等式最小的數}

對i從0到3-1求和就得所有輪換的元素個數為φ(3^s) . 3^s為素數,因此φ(3^s)=3^s-1,即覆蓋了所有元素.

 

因此很容易得出各輪換的代表元就為3^0,3^1,3^2......3^i(i<k,i+1>k).

對於2*n不等於3^k-1的情況,可以巧妙的利用這個結論完成ferfect shuffle。

對於2*n不等於3^k-1時,先找一個最接近2*n且比2*n小的2*m=3^k-1。進行如下變換。

把序列中m+1到n+m的子序列迴圈右移m位。

A_1,A_2,A_3.......A_m-1,A_m,A_m+1......A_n,A_n+1,A_n+2,.......A_n+m,A_n+m+1.....A_2*n   ->

A_1,A_2,A_3.......A_m-1,A_n+1,A_n+2.....A_n+m,A_m,A_m+1......A_n+m+1................A_2*n

然後對前2*m子序列進行上面的perfect shuffle。然後對剩下的部分進行同樣處理。

例如對於長為14的序列進行perfect shuffle置換:

輸入序列為:

1  2  3  4  5  6  7  A  B  C  D  E  F  G          

14=2*7≠3^k.與14最接近的3^k-1是8=3^2 - 1.因此先對4+1到7+4的子序列迴圈右移4位得:

1  2  3  4  A  B  C  D  5  6  7  E  F  G

對前8位進行perfect shuffle移位後得:

A  1  B  2  C  3  D  4  5  6  7  E  F  G

剩下的子序列為

5  6  7  E  F  G  

長度為6 最接近的2*m1=3^k1-1是 m=1

因此對 1+1 到3+1進行迴圈右移1位得

5  E  6  7  F  G

進行2*m的perfect shuffle後得整個序列為:

A  1  B  2  C  3   D  4   E   5    6   7   F   G

剩下的未處理的子序列為:

6   7   F   G

同樣的迴圈移位後為:

6   F   7   G

進行m=1的perfect shuffle得整個序列為:

A  1  B  2  C  3  D  4  E  5  F  6   7  G

剩下未處理的子序列為

7   G

長為2的輪換即交換,最後得整個序列為:

A  1  B  2  C  3  D  4  E  5  F  6  G  7

完成perfect shuffle。

移位是線性時間,3^k - 1的perfect shuffle置換也是線性時間,最後的遞迴是對剩下的子序列進行同樣的操作,因此整個過程線上性時間內完成。而且需要的輔助空間為常數-個額外臨時變數。

 

實現代碼:

#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]);

}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.