优雅地洗牌Javascript数组

优雅地洗牌Javascript数组,javascript,sorting,shuffle,Javascript,Sorting,Shuffle,有一些关于洗牌JS数组的现有线程,但它们似乎都不平凡,也不优雅,由许多行代码组成 我在一些博客上看到这样一句话:“变通方法”: 如果您不熟悉排序算法的工作原理,我将在这里简要解释,它总是关于比较数组的两个值,然后做出决定。建议的解决方案使用另一个函数“覆盖”默认比较函数,该函数为两个单元格的每次比较生成一个随机值(在每次比较中随机决定哪个大于哪个) 问题是,我不知道每个web浏览器使用哪种排序算法。对于某些排序算法,例如,BubbleSort,这样的函数可能会导致排序算法永远运行,因为它有1/2

有一些关于洗牌JS数组的现有线程,但它们似乎都不平凡,也不优雅,由许多行代码组成

我在一些博客上看到这样一句话:“变通方法”:

如果您不熟悉排序算法的工作原理,我将在这里简要解释,它总是关于比较数组的两个值,然后做出决定。建议的解决方案使用另一个函数“覆盖”默认比较函数,该函数为两个单元格的每次比较生成一个随机值(在每次比较中随机决定哪个大于哪个)

问题是,我不知道每个web浏览器使用哪种排序算法。对于某些排序算法,例如,BubbleSort,这样的函数可能会导致排序算法永远运行,因为它有1/2^(长度)的概率在不进行任何交换的情况下运行。对于快速排序,它似乎也有问题。我认为只有当web浏览器使用MergeSortHeapSort

有没有人尝试过它,并且可以判断它是否安全,或者推荐另一种解决方案?

这个“解决方案”是个糟糕的主意。它的效率很低(需要调用更多的
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;
时间