我有一个这样的数组:
var arr1 = ["a", "b", "c", "d"];
我怎样才能随机化/洗牌呢?
我有一个这样的数组:
var arr1 = ["a", "b", "c", "d"];
我怎样才能随机化/洗牌呢?
事实上的无偏洗牌算法是 Fisher-Yates(又名 Knuth)洗牌。
见https://github.com/coolaj86/knuth-shuffle
你可以在这里看到一个很棒的可视化(以及链接到这个的原始帖子)
function shuffle(array) {
let currentIndex = array.length, randomIndex;
// While there remain elements to shuffle...
while (currentIndex != 0) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
// And swap it with the current element.
[array[currentIndex], array[randomIndex]] = [
array[randomIndex], array[currentIndex]];
}
return array;
}
// Used like so
var arr = [2, 11, 37, 42];
shuffle(arr);
console.log(arr);
关于所用算法的更多信息。
这是Durstenfeld shuffle的 JavaScript 实现,这是 Fisher-Yates 的优化版本:
/* Randomize array 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;
}
}
它为每个原始数组元素挑选一个随机元素,并将其从下一次抽奖中排除,就像从一副纸牌中随机挑选一样。
这种巧妙的排除将选择的元素与当前元素交换,然后从余数中选择下一个随机元素,向后循环以获得最佳效率,确保随机选择被简化(它总是可以从 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]];
}
}
您可以使用 map 和 sort 轻松完成:
let unshuffled = ['hello', 'a', 't', 'q', 1, 2, 3, {cats: true}]
let shuffled = unshuffled
.map((value) => ({ value, sort: Math.random() }))
.sort((a, b) => a.sort - b.sort)
.map(({ value }) => value)
您可以对多态数组进行 shuffle,排序与 Math.random 一样随机,这对于大多数用途来说已经足够了。
由于元素根据每次迭代都不会重新生成的一致键进行排序,并且每次比较都从相同的分布中提取,因此 Math.random 分布中的任何非随机性都被抵消了。
速度
时间复杂度为 O(N log N),与快速排序相同。空间复杂度为 O(N)。这不如 Fischer Yates shuffle 有效,但在我看来,代码明显更短且功能更多。如果你有一个大数组,你当然应该使用 Fischer Yates。如果你有一个包含几百个项目的小数组,你可以这样做。
警告!不推荐
使用这种算法,因为它效率低下且有很强的偏见;看评论。它被留在这里以备将来参考,因为这种想法并不罕见。
[1,2,3,4,5,6].sort( () => .5 - Math.random() );
这个https://javascript.info/array-methods#shuffle-an-array教程直接解释了差异。
人们可以(或应该)将其用作 Array 的原型:
来自克里斯托夫:
Array.prototype.shuffle = function() {
var i = this.length, j, temp;
if ( i == 0 ) return this;
while ( --i ) {
j = Math.floor( Math.random() * ( i + 1 ) );
temp = this[i];
this[i] = this[j];
this[j] = temp;
}
return this;
}