优雅地洗牌Javascript数组
有一些关于洗牌JS数组的现有线程,但它们似乎都不平凡,也不优雅,由许多行代码组成 我在一些博客上看到这样一句话:“变通方法”: 如果您不熟悉排序算法的工作原理,我将在这里简要解释,它总是关于比较数组的两个值,然后做出决定。建议的解决方案使用另一个函数“覆盖”默认比较函数,该函数为两个单元格的每次比较生成一个随机值(在每次比较中随机决定哪个大于哪个) 问题是,我不知道每个web浏览器使用哪种排序算法。对于某些排序算法,例如,BubbleSort,这样的函数可能会导致排序算法永远运行,因为它有1/2^(长度)的概率在不进行任何交换的情况下运行。对于快速排序,它似乎也有问题。我认为只有当web浏览器使用MergeSort或HeapSort 有没有人尝试过它,并且可以判断它是否安全,或者推荐另一种解决方案?这个“解决方案”是个糟糕的主意。它的效率很低(需要调用更多的优雅地洗牌Javascript数组,javascript,sorting,shuffle,Javascript,Sorting,Shuffle,有一些关于洗牌JS数组的现有线程,但它们似乎都不平凡,也不优雅,由许多行代码组成 我在一些博客上看到这样一句话:“变通方法”: 如果您不熟悉排序算法的工作原理,我将在这里简要解释,它总是关于比较数组的两个值,然后做出决定。建议的解决方案使用另一个函数“覆盖”默认比较函数,该函数为两个单元格的每次比较生成一个随机值(在每次比较中随机决定哪个大于哪个) 问题是,我不知道每个web浏览器使用哪种排序算法。对于某些排序算法,例如,BubbleSort,这样的函数可能会导致排序算法永远运行,因为它有1/2
Math.random()
),而且,正如您所指出的,用于某些排序算法是危险的。这也是有偏见的(至少对于我的nodeJS安装中的sort()
版本,尽管我无法解释原因)。下面是对数组进行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次 对于无偏洗牌,这六种排列的平均频率应该相同(大约10000次,重复60000次)。在我的实验中,[1,2,3]和[2,1,3]出现的频率大约是其他四种排列的两倍。这在多次测试中是一致的 您最好坚持使用标准的Fisher-Yates shuffle(在和web上的许多其他地方都有描述)。在伪代码中,如下所示(摘自维基百科文章): 不太复杂,肯定是更好的方法。这是我的JavaScript版本(可能需要清理一下): 请注意,以下是我用来测试您的变通方法的代码:
yourArray.sort(function() { return 0.5 - Math.random() });
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));
函数shuffleRandomSort(a){
a、 排序(函数(){return 0.5-Math.random()});
返回a;
}
功能分数(分数、结果){
var指数;
如果(结果[0]==1){
索引=结果[1]==2
? 0 // [1, 2, 3]
: 1; // [1, 3, 2]
}else if(结果[0]==2){
索引=结果[1]==1
? 2 // [2, 1, 3]
: 3; // [2, 3, 1]
}否则{//结果[0]==3
索引=结果[1]==1
? 4 // [3, 1, 2]
: 5; // [3, 2, 1]
}
分数[指数]+;
}
函数运行测试(shuffler,n){
var得分=[0,0,0,0,0,0],
A.
对于(变量i=0;i
这个“变通办法”是个糟糕的主意。它的效率很低(需要调用更多的Math.random()
),而且,正如您所指出的,用于某些排序算法是危险的。这也是有偏见的(至少对于我的nodeJS安装中的sort()
版本,尽管我无法解释原因)。下面是对数组进行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次 对于无偏洗牌,这六种排列的平均频率应该相同(大约10000次,重复60000次)。在我的实验中,[1,2,3]和[2,1,3]出现的频率大约是其他四种排列的两倍。这在多次测试中是一致的 您最好坚持使用标准的Fisher-Yates shuffle(在和web上的许多其他地方都有描述)。在伪代码中,如下所示(摘自维基百科文章): 不太复杂,肯定是更好的方法。这是我的JavaScript版本(可能需要清理一下): 请注意,以下是我用来测试您的变通方法的代码:
yourArray.sort(function() { return 0.5 - Math.random() });
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));
函数shuffleRandomSort(a){
a、 排序(函数(){return 0.5-Math.random()});
返回a;
}
功能分数(分数、结果){
var指数;
如果(结果[0]==1){
索引=结果[1]==2
? 0 // [1, 2, 3]
: 1; // [1, 3, 2]
}else if(结果[0]==2){
索引=结果[1]==1
? 2 // [2, 1, 3]
: 3; // [2, 3, 1]
}否则{//结果[0]==3
索引=结果[1]==1
? 4 // [3, 1, 2]
: 5; // [3, 2, 1]
}
分数[指数]+;
}
函数运行测试(shuffler,n){
var得分=[0,0,0,0,0,0],
A.
对于(变量i=0;i
我抓取了一些算法,并用控制台对它们进行了测试。时间
,您可以看到运行它们的结果:
var smallArray=Array.from({length:10},(x,i)=>i);
var bigArray=Array.from({length:1000},(x,i)=>i);
函数shuffle1(数组){
var currentIndex=array.length,temporaryValue,randomIndex;
while(当前索引){
randomIndex=(Math.random()*currentIndex)| 0;
currentIndex-=1;
时间