洗牌算法shuffle

2023-06-12,,

  对这个问题的研究始于一次在群里看到朋友发的洗牌面试题。当时也不知道具体的解法如何,于是随口回了一句:每次从剩下的数字中随机一个。过后找相关资料了解了下,洗牌算法大致有3种,按发明时间先后顺序如下:

一、Fisher–Yates Shuffle

算法思想就是从原始数组中随机抽取一个新的数字到新数组中。算法英文描述如下:

Write down the numbers from 1 through N.
Pick a random number k between one and the number of unstruck numbers remaining (inclusive).
Counting from the low end, strike out the kth number not yet struck out, and write it down elsewhere.
Repeat from step 2 until all the numbers have been struck out.
The sequence of numbers written down in step 3 is now a random permutation of the original numbers.

python实现代码如下:

#Fisher–Yates Shuffle
'''
1. 从还没处理的数组(假如还剩k个)中,随机产生一个[0, k]之间的数字p(假设数组从0开始);
2. 从剩下的k个数中把第p个数取出;
3. 重复步骤2和3直到数字全部取完;
4. 从步骤3取出的数字序列便是一个打乱了的数列。
'''
import random def shuffle(lis):
result = []
while lis:
p = random.randrange(0, len(lis))
result.append(lis[p])
lis.pop(p)
return result r = shuffle([1, 2, 2, 3, 3, 4, 5, 10])
print(r)

二、Knuth-Durstenfeld Shuffle

  Knuth 和Durstenfeld 在Fisher 等人的基础上对算法进行了改进。每次从未处理的数据中随机取出一个数字,然后把该数字放在数组的尾部,即数组尾部存放的是已经处理过的数字。这是一个原地打乱顺序的算法,算法时间复杂度也从Fisher算法的O(n2)提升到了O(n)。算法伪代码如下:

以下两种实现方式的差异仅仅在于遍历的方向而已。下面用python实现前一个:

#Knuth-Durstenfeld Shuffle
def shuffle(lis):
for i in range(len(lis) - 1, 0, -1):
p = random.randrange(0, i + 1)
lis[i], lis[p] = lis[p], lis[i]
return lis r = shuffle([1, 2, 2, 3, 3, 4, 5, 10])
print(r)

三、Inside-Out Algorithm

Knuth-Durstenfeld Shuffle 是一个in-place算法,原始数据被直接打乱,有些应用中可能需要保留原始数据,因此需要开辟一个新数组来存储打乱后的序列。Inside-Out Algorithm 算法的基本思想是设一游标i从前向后扫描原始数据的拷贝,在[0, i]之间随机一个下标j,然后用位置j的元素替换掉位置i的数字,再用原始数据位置i的元素替换掉拷贝数据位置j的元素。其作用相当于在拷贝数据中交换i与j位置处的值。伪代码如下:

python代码实现如下:

#Inside-Out Algorithm
def shuffle(lis):
result = lis[:]
for i in range(1, len(lis)):
j = random.randrange(0, i)
result[i] = result[j]
result[j] = lis[i]
return result r = shuffle([1, 2, 2, 3, 3, 4, 5, 10])
print(r)

、后话

前面用python实现了三种洗牌算法,其实python random模块也有个shuffle方法,用法如下:

其内部实现正是使用Knuth-Durstenfeld Shuffle算法,不信您看代码:-):

参考:

http://en.wikipedia.org/wiki/Fisher-Yates_shuffle

洗牌算法shuffle的相关教程结束。

《洗牌算法shuffle.doc》

下载本文的Word格式文档,以方便收藏与打印。