从排序算法到洗牌算法:Fisher-Yates Shuffle算法

开发 前端
后来Knuth和Durstenfeld在该算法基础上进行了改变,在原始数组上对数字进行交换,从而节省了额外的数组空间。该算法的基本思想和Fisher算法类似,只不过每次从未处理的数据中随机取出一个数字,然后把该数字放在数组的尾部,即把已经处理的数字存放在数组尾部。Knuth-Durstenfeld Shuffle算法是当前最为常用的洗牌算法。​

排序算法对于每个程序员来说,无疑是最常见的算法之一了。几乎每个新入行的程序员,在面试之前都会准备好一两种排序算法,例如冒泡排序、归并排序、快速排序之类的。而面试官们很多也都会现场让应聘者写一个简单的排序算法,来考验对方的基本功怎么样。

排序算法是让无序的数据变得有序起来,而反过来,让有一定顺序的数据变得无序起来,那么这个算法就是洗牌算法。洗牌算法是生活中常见的一种基础算法,最简单的应用就是在打扑克牌的时候,随机对扑克牌进行初始化。这时候要用到的算法就是洗牌算法。

大家在看王晶的赌王片里,肯定对于各种赌王的花式洗牌方法记忆很深刻。直观上来讲,洗牌算法非常好理解,无非就是把一副牌打乱。但是,什么样的结果才叫乱,很多人则说不太清楚了。

数学上,一个好的洗牌算法需要达到的目标是:在洗过牌之后,任意一张牌出现在任意位置的概率是一样的。例如对于n个数的洗牌算法,那么一张牌出现在任一位置的概率都是1/n,而洗牌算法最终的结果一共有n!种,即为数据的全排列。

这个目标很好理解,如果某种算法给出的结果是黑桃A在第一张的概率是50%,那么搞不好庄家就要吃大亏了。

所以洗牌算法最重要的就是结果要够乱。

最为经典的洗牌算法是Fisher-Yates Shuffle算法。该算法由Ronald A. Fisher 和 Frank Yates两人提出,其步骤如下:

1、初始化原始数组和新数组,数组长度设为n;

2、从还没有处理的数据中(假如还剩下k个),随机产生一个[0, k)之间的数字p;

3、从剩下的k个数中把第p个数字取出,按顺序放入新数组;

4、重复步骤2和3直到数字全部取完;

5、此时新数组中即是洗牌之后的数组。

下面我们证明一下该算法具有足够的随机性,即每个元素被放置在新数组中的第i个位置的概率是1/n。

证明:一个元素m被放入第i个位置的概率P = 前i-1个位置选择元素时没有选中m的概率 * 第i个位置选中m的概率,即

图片

其中,第一次在n个中选没选到它,选中了另外n-1个的概率就是(n-1)/n,以此类推。

所以该算法具有足够的随机性。其时间复杂度为O(n*n),空间复杂度为O(n)。

后来Knuth和Durstenfeld在该算法基础上进行了改变,在原始数组上对数字进行交换,从而节省了额外的数组空间。该算法的基本思想和Fisher算法类似,只不过每次从未处理的数据中随机取出一个数字,然后把该数字放在数组的尾部,即把已经处理的数字存放在数组尾部。Knuth-Durstenfeld Shuffle算法是当前最为常用的洗牌算法。​

责任编辑:武晓燕 来源: 活在信息时代
相关推荐

2021-03-23 13:55:35

大数据算法

2013-07-08 09:30:32

排序算法

2021-11-05 22:47:44

冒泡排序选择插入

2023-10-05 09:01:05

插入排序对象序列log2i

2019-09-17 16:30:18

java排序算法

2015-08-26 10:13:55

排序算法总结

2022-02-11 09:42:21

Swift开发语言LeetCode

2023-04-27 09:13:20

排序算法数据结构

2020-09-14 14:47:13

排序算法

2014-10-30 15:14:54

快速排序编程算法

2012-01-09 14:29:15

Java算法

2011-04-20 14:07:37

冒泡排序

2011-04-20 14:19:00

希尔排序

2011-04-20 13:56:08

选择排序

2012-10-16 09:40:38

洗牌算法

2019-03-29 09:40:38

数据结构算法前端

2024-01-30 17:48:43

算法字符串性能

2022-01-06 16:20:04

Java排序算法排序

2014-10-30 15:59:10

2021-07-09 09:12:40

STL排序算法
点赞
收藏

51CTO技术栈公众号