【问题标题】:How can I shuffle an array? [duplicate]我怎样才能洗牌一个数组? [复制]
【发布时间】:2011-09-10 14:05:37
【问题描述】:

我想在 JavaScript 中洗牌一个元素数组,如下所示:

[0, 3, 3] -> [3, 0, 3]
[9, 3, 6, 0, 6] -> [0, 3, 6, 9, 6]
[3, 3, 6, 0, 6] -> [0, 3, 6, 3, 6]

【问题讨论】:

  • 这个问题已经在stackoverflow上回答了很多次。检查stackoverflow.com/questions/2450954/…这是另一个:stackoverflow.com/questions/5086262/…
  • JavaScript Shuffle, Deal, Draw 和其他日期和数学资料的好资源。
  • 单线怎么样?返回的数组被打乱。 arr1.reduce((a,v)=>a.splice(Math.floor(Math.random() * a.length), 0, v) && a, [])
  • @VitaliPom 不要将 sort() 与 random() 一起使用。排序不期望随机结果,结果可能不统一。正因为如此,微软的浏览器投票是bugged
  • @brunettdan 我写了一个不使用拼接并且速度更快的衬里:arr1.reduceRight((p,v,i,a)=>(v=i?~~(Math.random()*(i+1)):i, v-i?[a[v],a[i]]=[a[i],a[v]]:0, a),a);另请查看this function

标签: javascript


【解决方案1】:

使用the modern version of the Fisher–Yates shuffle algorithm:

/**
 * Shuffles array in place.
 * @param {Array} a items An array containing the items.
 */
function shuffle(a) {
    var j, x, i;
    for (i = a.length - 1; i > 0; i--) {
        j = Math.floor(Math.random() * (i + 1));
        x = a[i];
        a[i] = a[j];
        a[j] = x;
    }
    return a;
}

ES2015 (ES6) 版本

/**
 * Shuffles array in place. ES6 version
 * @param {Array} a items An array containing the items.
 */
function shuffle(a) {
    for (let i = a.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [a[i], a[j]] = [a[j], a[i]];
    }
    return a;
}

但请注意,截至 2017 年 10 月,使用 destructuring assignment 交换变量会导致显着的性能损失。

使用

var myArray = ['1','2','3','4','5','6','7','8','9'];
shuffle(myArray);

实现原型

使用Object.defineProperty (method taken from this SO answer),我们还可以将这个函数实现为数组的原型方法,而不会出现在for (i in arr)等循环中。以下将允许您调用arr.shuffle() 对数组arr 进行洗牌:

Object.defineProperty(Array.prototype, 'shuffle', {
    value: function() {
        for (let i = this.length - 1; i > 0; i--) {
            const j = Math.floor(Math.random() * (i + 1));
            [this[i], this[j]] = [this[j], this[i]];
        }
        return this;
    }
});

【讨论】:

  • 这个方法(以及下面的方法)都修改了原始数组。这没什么大不了的,但是如何调用它的例子有点奇怪。
  • @Michael +1 指出重新分配是不必要的。事实上,它具有误导性,并且可能应该是该评论线程中指出的第一件事。
  • 我发现 ES6 交换速度较慢(一旦我开始使用它。你必须在 [-- 更有理由总是使用它们之前有一个分号。)。
  • @trlkly:由于使用了解构赋值,ES2015 变体会变慢。希望引擎能尽快优化它。
  • @RobG const 是一个完美的选择,因为它是块范围的,与 var 不同,并且在每次交互后都会重新声明。 let 也可以,但由于 j 不会改变它的值,因此块 const 是更好的选择
【解决方案2】:

您可以使用Fisher-Yates Shuffle(代码改编自this site):

function shuffle(array) {
    let counter = array.length;

    // While there are elements in the array
    while (counter > 0) {
        // Pick a random index
        let index = Math.floor(Math.random() * counter);

        // Decrease counter by 1
        counter--;

        // And swap the last element with it
        let temp = array[counter];
        array[counter] = array[index];
        array[index] = temp;
    }

    return array;
}

【讨论】:

  • 第一个答案似乎有一个错误。大约每 15 次运行一次,我会得到一个额外的 undefined 列。 jsfiddle.net/tomasswood/z8zm7
  • 为什么不使用 random + Array.prototype.sort?它比这两个答案都更简单,代码更少。
  • @Volter9:因为分布不会是均匀的。
  • 杰夫阿特伍德关于这个算法的非常有趣的帖子。 blog.codinghorror.com/the-danger-of-naivete我想知道为什么它是这样实现的
  • 请注意,这会更改初始数组,而不是功能性方法。只是把这个留给任何盲目复制这个的人(就像我做的那样,哈哈)。
猜你喜欢
  • 2014-11-03
  • 1970-01-01
  • 2014-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-25
相关资源
最近更新 更多