![8d617b995357f3ac580637cda36a96ce.png](https://img-blog.csdnimg.cn/img_convert/8d617b995357f3ac580637cda36a96ce.png)
「洗牌」,或者说随机打乱一个数组中元素的顺序,是编程中的一个常见需求。标准的洗牌算法是 Fisher-Yates shuffle,用 JavaScript 实现如下:
function shuffle(A) {
for (var i = A.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var t = A[i]; A[i] = A[j]; A[j] = t;
}
}
其基本思路是,每次从未打乱的部分等可能地选一个元素,把它与未打乱部分的最后一个元素交换。
Fisher-Yates 洗牌算法的实现十分简单,并且它可以保证均匀性,即元素的各种排列顺序出现的概率都相等。但是,很多人闭门造车地发明了一些「错误」的洗牌算法实现,它们不能保证均匀。例如,最常见的一种错误实现如下:
function shuffle(A) {
for (var i = 0; i < A.length; i++) {
var j = Math.floor(Math.random() * A.length);
var t = A[i]; A[i] = A[j]; A[j] = t;
}
}
其原理是,在第 i 次循环中,从所有元素中等可能地选一个元素,与第 i 个元素交换。这种算法的错误可以如下证明:对于一个长度为
另一种错误的洗牌算法是 @Lucas HC 在这篇答案中指出的:
A.sort(function() {
return .5 - Math.random();
});
JavaScript 中数组自带的 sort 方法允许提供一个比较器,其返回值的正负号代表两个元素的大小关系。在上面的代码中,比较器返回的是 -0.5 到 0.5 之间的一个随机数,也就是说每次比较的结果是随机且均匀的。但是,基于随机比较的整个洗牌算法是不均匀的:它的各种运行结果的概率都形如