洗牌算法 | Fisher–Yates shuffle Algorithm

洗牌算法,近年来已经逐渐成为互联网公司面试题中常见的一类,今天我们就来聊一聊洗牌算法中经典且简单的一种 - Fisher–Yates shuffle Algorithm。

给定一个数组,编写一个程序来生成数组元素的随机排列。这个问题也被称为“洗牌”或“随机化给定的数组”。这里的 shuffle 意味着数组元素的每一种可能的排列都应该有相同的发生概率。 

设给定的数组为arr[]。一个简单的解决方案是创建一个辅助数组temp[],它最初是arr[]的副本。从temp[]中随机选择一个元素,将随机选择的元素复制到arr[0],然后从temp[]中删除选择的元素。重复相同的过程 n 次并继续将元素复制到arr[1]、arr[2]、...。该解决方案的时间复杂度为 O(n^2)。

上述的方法虽然简单且容易理解,但O(n^2)的时间复杂度显然不是我们想要的。

Fisher–Yates洗牌算法的工作时间复杂度为 O(n)。我们需要做一个假设,那就是我们有一个随机函数 rand(),它可以在 O(1)的时间内生成一个随机数。算法的思想是从数组的最后一个元素开始,并将其与整个数组(包括最后一个)中随机选择的元素交换,之后在 0 到 n-2 的数组(大小减 1),重复之前的操作直到我们到达第一个元素。

具体算法如下

图解

算法原理

第 i 个元素(包括最后一个)到达最后一个位置的概率是 1/n,因为我们在第一次迭代中随机选择了一个元素。
将第 i 个元素分为两种情况,可以证明第 i 个元素到达倒数第二个位置的概率也为 1/n。 
1:i = n-1(最后一个元素的索引): 
最后一个元素到达倒数第二个位置的概率是=(最后一个元素不停留在其原始位置的概率)x(上一个索引选择的概率再次选择 step 以便交换最后一个元素) 
所以概率 = ((n-1)/n) x (1/(n-1)) = 1/n 
情况 2:0 < i < n-1 (非最后一个索引): 
第 i 个元素到达倒数第二个位置的概率 = (第 i 个元素在前一次迭代中未被选中的概率) x (第 i 个元素在本次迭代中被选中的概率) 
所以概率 = ((n-1)/n) x (1 /(n-1)) = 1/n
我们可以很容易地将上述证明推广到任何其他位置。

注意:该算法是建立在每个位置的元素被选中的概率都为1/n的假设上的,也就是rand()抽中每个元素的概率必须相等

Fisher–Yates shuffle Algorithm的Python实现

from random import randint


def randomize (arr, n):
	
	for i in range(n-1,0,-1):
		# Pick a random index from 0 to i
		j = randint(0,i+1)

		# Swap arr[i] with the element at random index
		arr[i],arr[j] = arr[j],arr[i]
	return arr


arr = [1, 2, 3, 4, 5, 6, 7, 8]
n = len(arr)
print(randomize(arr, n))


  其他语言实现下载链接:

(包含各种语言:C、Python、Java、C++、C#、PHP等均有示例)

免费​资源下载:Fisher–Yates

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值