在Jon已经涵盖了这个理论之后,这里有一个实现:
function shuffle(array) {
var tmp, current, top = array.length;
if(top) while(--top) {
current = Math.floor(Math.random() * (top + 1));
tmp = array[current];
array[current] = array[top];
array[top] = tmp;
}
return array;
}
算法是 ,而排序应该是 。根据执行 JS 代码与本机函数相比的开销,这可能会导致明显的性能差异,这应该随着数组大小而增加。O(n)
O(n log n)
sort()
在对bobobobo的答案的评论中,我指出所讨论的算法可能不会产生均匀分布的概率(取决于的实现)。sort()
我的论点是这样的:排序算法需要一定数量的比较,例如对于Bubblesort。我们的随机比较函数使每个比较的结果具有同等的可能性,即存在同样可能的结果。现在,每个结果都必须对应于数组条目的排列之一,这使得在一般情况下不可能进行偶数分布。(这是一种简化,因为实际比较次数取决于输入数组,但断言仍然应该成立。c
c = n(n-1)/2
2^c
n!
正如 Jon 所指出的,仅凭这一点并不是选择 Fisher-Yates 而不是使用的理由,因为随机数生成器也会将有限数量的伪随机值映射到排列中。但Fisher-Yates的结果应该仍然更好:sort()
n!
Math.random()
在 范围内生成一个伪随机数。由于JS使用双精度浮点值,这对应于可能的值(我懒得找到实际数字)。如果原子事件的数量具有相同的数量级,则 使用 生成的概率分布将停止运行良好。[0;1[
2^x
52 ≤ x ≤ 63
Math.random()
使用Fisher-Yates时,相关参数是数组的大小,由于实际限制,永远不应该接近。2^52
当使用随机比较函数进行排序时,该函数基本上只关心返回值是正数还是负数,因此这永远不会成为问题。但也有类似的一个:因为比较函数表现良好,所以可能的结果,如前所述,同样可能。如果那么 where ,这使得它至少有可能具有相同的量级(甚至小于),从而导致分布不均匀,即使排序算法在哪里均匀地映射到排列上。如果这有什么实际影响,那将超出我。2^c
c ~ n log n
2^c ~ n^(a·n)
a = const
2^c
n!
真正的问题是排序算法不能保证均匀地映射到排列上。很容易看出Mergesort是对称的,但是像Bubblesort或更重要的是Quicksort或Heapsort这样的东西的推理却不是。
底线:只要使用Mergesort,除了在角落的情况下(至少我希望这是一个角落的情况),你应该是相当安全的,如果没有,所有的赌注都被取消了。sort()
2^c ≤ n!