由抽牌、换牌和插牌衍生出三种洗牌算法,其中分别对应Fisher-Yates Shuffle、Knuth-Durstenfeld Shhuffle、Inside-Out Algorithm算法。
今天介绍Fisher-Yates Shuffle洗牌算法。最早提出这个洗牌方法的是 Ronald A Fisher 和 Frank Yates,即 Fisher–Yates Shuffle,其基本思想就是从原始数组中随机取一个之前没取过的数字到新的数组中。
下面证明其随机性,即每个元素被放置在新数组中的第i个位置是1/n(假设数组大小是n)。
证明:一个元素m被放入第i个位置的概率P = 前i-1个位置选择元素时没有选中m的概率 * 第i个位置选中m的概率,即:
具体如下,我们简单借助图形来理解,非常简单直观。你接下来就会明白为什么这是理论上的完全乱序(图片来源于网络)。
首先我们有一个已经排好序的数组:
Step1:
第一步需要做的就是,从数组末尾开始,选取最后一个元素。
在数组一共 9 个位置中,随机产生一个位置,该位置元素与最后一个元素进行交换。
Step2:
上一步中,我们已经把数组末尾元素进行随机置换。
接下来,对数组倒数第二个元素动手。在除去已经排好的最后一个元素位置以外的8个位置中,随机产生一个位置,该位置元素与倒数第二个元素进行交换。
Step3:
理解了前两部,接下来就是依次进行,如此简单。
最后,我们实现代码:
Array.prototype.shuffle = function() {
var array = this;
var m = array.length,
t, i;
while (m) {
i = Math.floor(Math.random() * m--);
t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
}
将当前元素和随机选出的下标所指的元素互相交换,其中选取下标范围的依据在于每个被摸出的元素都不可能再被摸出来了。此外还有一个需要注意的细节,当前元素是可以和它本身互相交换的 - 否则生成最后的排列组合的概率就不对了。