事实上的无偏差洗牌算法是 Fisher-Yates(aka Knuth)Shuffle。
请参阅https://github.com/coolaj86/knuth-shuffle
你可以在这里看到一个很棒的可视化 (以及链接到此的原始帖子)
function shuffle(array) {
var currentIndex = array.length, temporaryValue, randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
// Used like so
var arr = [2, 11, 37, 42];
arr = shuffle(arr);
console.log(arr);
有关所用算法的更多信息。
这是Durstenfeld shuffle的 JavaScript 实现,这是 Fisher-Yates 的计算机优化版本:
/**
* Randomize array element order in-place.
* Using Durstenfeld shuffle algorithm.
*/
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
Fisher-Yates 算法通过为每个原始数组元素选择一个随机元素,然后从下一个绘制中排除它来工作。就像从一副牌中随机挑选一样。
这种排除是以巧妙的方式完成的(由 Durstenfeld 发明供计算机使用),方法是将拾取的元素与当前元素交换,然后从剩余部分中挑选下一个随机元素。为了获得最佳效率,循环向后运行以便随机选择被简化(它总是从 0 开始),并且它跳过最后一个元素,因为不再有其他选择。
该算法的运行时间为 O(n)。请注意,shuffle 是就地完成的。因此,如果您不想修改原始数组,请先使用.slice(0)
复制它。
新的 ES6 允许我们一次分配两个变量。当我们想要交换两个变量的值时,这尤其方便,因为我们可以在一行代码中完成。这是使用此功能的相同功能的缩写形式。
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
[社区编辑:这个答案是不正确的; 看评论。它留在这里供将来参考,因为这个想法并不罕见。]
[1,2,3,4,5,6].sort(function() {
return .5 - Math.random();
});