'''
#完美洗牌算法
长度为2n的数组{a1,a2,...,an,b1,b2,...,bn},经过整理后变成{a1,b1,a2,b2,...,an,bn},要求时间复杂度O(n),空间复杂度O(1)
1.步步前移
观察变换后两个序列的特点,我们可以做如下一系列操作:
1.确定b1的位置,即让它跟它前面的a2,a3,a4交换:
a1,b1,a2,a3,a4,b2,b3,b4
2.接着确定b2的位置,即让b2跟它前面的a3,a4交换:
a1,b1,a2,b2,a3,a4,b3,b4
3.b3跟它前面的a4交换位置:
a1,b1,a2,b2,a3,b3,a4,b4
b4无需交换
移动n-1次,第i次将n-i个元素后移。时间复杂度为O(N^2)
2.中间交换
每次让序列中最中间的元素进行交换
对于a1,a2,a3,a4,b1,b2,b3,b4
1.交换最中间的两个元素a4,b1:
a1,a2,a3,b1,a4,b2,b3,b4
2.让最中间的两对元素各自交换[(a3,b1),(a4,b2)]:
a1,a2,b1,a3,b2,a4,b3,b4
3.交换,最中间的三对元素[(a2,b1),(a3,b2),(a4,b3)]:
a1,b1,a2,b2,a3,b3,a4,b4
时间复杂度:n-1+n-2+...+1=n^2/2--O(N^2)
'''
'''
完美洗牌算法:2004年,macrosoft的Peiyush Jain在其发表的一篇名为"A Simple In-Place Algorithm for In-Shuffle"的论文中提出。
位置变换
a1,a2,...,an,b1,b2,...,bn-->b1,a1,b2,a2,...,bn,an
设定数组下标范围[1,2n],考察元素的最终位置:
以n=4为例,前n个元素中
第1个元素a1到了原第2个元素a2的位置,即1->2
第2个元素a2到了原第4个元素a4的位置,即2->4
第3个元素a3到了原第6个元素b2的位置,即3->6
第4个元素a4到了原第8个元素b4的位置,即4->8
前n个元素中,第i个元素的最终位置为(2*i)
后n个元素,可以看出:
第5个元素b1到了原第1个元素a1的位置,即5->1
第6个元素b2到了原第3个元素a3的位置,即6->3
第7个元素b3到了原第5个元素b1的位置,即7->5
第8个元素b4到了原第7个元素b3的位置,即8->7
后n个元素,第i个元素的最终位置为:2*(i-n)-1=2*i-2*n-1=(2*i)%(2*n+1)
因此元素位置均可表述为 (2*i)%(2*n+1)
两个圈
我们得到两个圈
1->2->4->8->7->5->1
3->6->3
'''
#数组下标从1开始,f是圈的头部,mod为2*n+1
def cycleLeader(a,f,mod):
# for i in range(f*2%mod,5,2*i/mod)
i=2*f%mod
while(i!=f):
t=a[i-1]
a[i-1]=a[f-1]
a[f-1]=t
# print(a[i-1],a[f-1])
i=2*i%mod
# print(a)
# l2=[1,2,3,4,5,6,7,8,9,10]
# mod=len(a)+1
# cycleLeader(l2,1,mod)
'''
K个圈:对于2*n=(3^k-1)这种长度的数组,恰好只有k个圈,且每个圈的起始位置分别是1,3,9,...3^(k-1)
对于2n!=(3^k-1)长度的数组,
找到最大2m=(3^k-1)的部分,且3^k<=2*n<3^(k+1)
把a[m+1...m+n]部分循环右移m为(循环左移n-m位)
对每个i=0,1,2,...,k-1,3^i是每个圈的起始位置,做cycleLeader算法
因为子数组长度为m,所以对2*m+1取模
对数组的剩余部分A[2m+1...2n]继续使用本算法
'''
def reverseString(s,f,to):
while (f<to):
# print(f)
t=s[f]
s[f]=s[to]
s[to]=t
f+=1
to-=1
# print(s)
def leftRotateString(s,n,m):
m%=n
reverseString(s,0,m-1)
reverseString(s,m,n-1)
reverseString(s,0,n-1)
# print(''.join(s))
# print(s)
def perfectShuffle2(a,n):
# print(n)
final=[]
while(n>1):
n2=n*2
m=1
k=0
while((n2+1)/m>=3):
k+=1
m*=3
m=math.floor(m/2)
# print(m)
t1=a[:m]
t2=a[m:m+n]
t3=a[m+n:]
leftRotateString(t2,n,n-m)
i=0
t=1
a=t1+t2+t3
# print(a)
# print(k)
while(i<k):
cycleLeader(a,t,m*2+1)
i+=1
t*=3
final+=a[:2*m]
a=a[2*m:]
n-=m
if n==1:#仅剩两个元素时
t=a[0]
a[0]=a[1]
a[1]=t
final+=a
print(final)
l3=[1,2,3,4,5,6,7,8,'a','b','c','d','e','f','g','h']
# l3=[1,2,3,4,5,'a','b','c','d','e']
perfectShuffle2(l3,int(len(l3)/2))
算法原理图示:
部分原理解释及问题拓展: