“解决方法”是一个可怕的想法。这是效率低下的(使用更多的Math.random()
而不是必要的),正如你所说的,对于一些排序算法是很危险的。这也是有偏见的(至少对于我的nodeJS安装中的sort()
版本,尽管我无法解释为什么)。这里的分选阵列[1, 2, 3]
60000倍和计数多少每个排列的典型结果显示了:
[1,2,3]:14821倍
[1,3,2]:7637倍
[2,1,3]:15097倍
[2,3,1]:7590倍
[3,1,2]:7416倍
[3,2,1]:7439倍
对于无偏差洗牌,六个排列应该出现eq平均而言常常是6万次重复的约10000次。在我的实验中,[1,2,3]和[2,1,3]出现的频率大约是其他四种排列的两倍。这在测试的许多次运行中都是一致的。
你好得多粘到标准费雪耶茨洗牌(在网络上的this Wikipedia article和其他许多地方所描述的)。在伪代码,它看起来像这样(从维基百科的文章取):
-- To shuffle an array a of n elements (indices 0..n-1):
for i from n−1 downto 1 do
j ← random integer such that 0 ≤ j ≤ i
exchange a[j] and a[i]
不要太复杂,绝对是一个更好的办法。这是我的JavaScript版本(可能会清理一下):
function shuffleFisherYates(a) {
var i, j, tmp;
for (i = a.length - 1; i > 0; --i) {
j = Math.floor(Math.random() * (i + 1));
tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
return a;
}
P.S.作为参考,这里是我用来测试你的解决方法的代码:
function shuffleRandomSort(a) {
a.sort(function() { return 0.5 - Math.random() });
return a;
}
function score(scores, result) {
var index;
if (result[0] === 1) {
index = result[1] === 2
? 0 // [1, 2, 3]
: 1; // [1, 3, 2]
} else if (result[0] === 2) {
index = result[1] === 1
? 2 // [2, 1, 3]
: 3; // [2, 3, 1]
} else { // result[0] === 3
index = result[1] === 1
? 4 // [3, 1, 2]
: 5; // [3, 2, 1]
}
scores[index]++;
}
function runTest(shuffler, n) {
var scores = [0, 0, 0, 0, 0, 0],
a;
for (var i = 0; i < n; ++i) {
a = [1, 2, 3];
score(scores, shuffler(a));
}
console.log(scores);
}
console.log(shuffleRandomSort, runTest(60000));
与ES6相同的Fisher-Yates shuffle算法要慢得多:/ – BrunoLM
是的,1和3都是Fisher-Yates。由于结构化的分配(我猜在大多数浏览器中没有很好的优化),版本3可能会比较慢。与手动编码的循环相比,我很惊讶lodash shuffle的表现很差。那可能是因为你的测试缺乏热身循环? –