我怎样才能洗牌一个数组?

IT技术 javascript
2020-12-28 16:31:29

我想对 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]
2个回答

使用现代版本的 Fisher-Yates shuffle 算法

/**
 * 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 月,使用解构赋值交换变量会导致显着的性能损失。

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

实现原型

使用Object.defineProperty从这个 SO 答案中获取的方法)我们还可以将此函数实现为数组的原型方法,而不会出现在诸如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 指出重新分配是不必要的。事实上,它具有误导性,可能应该是此评论线程中指出的第一件事。
2021-02-10 16:31:29
@RobG const 是一个完美的选择,因为它是块范围的,与 var 不同,并且在每次交互后重新声明。let 也可以工作,但由于 j 不会改变它的内部值,因此块 const 是更好的选择
2021-02-14 16:31:29
这种方法(以及下面的方法)都修改了原始数组。这没什么大不了的,但是如何调用它的示例有点奇怪。
2021-02-25 16:31:29
我发现 ES6 交换速度较慢(一旦我开始使用它。你必须在 [-- 更有理由总是使用它们之前有一个分号。)。
2021-03-02 16:31:29
@trlkly:由于使用了解构赋值,ES2015 变体会变慢。希望引擎能尽快优化它。
2021-03-07 16:31:29

您可以使用Fisher-Yates Shuffle(改编自本网站的代码):

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;
}
为什么你不使用随机 + Array.prototype.sort?与这两个答案相比,它更容易且代码更少。
2021-02-10 16:31:29
@Volter9:因为分布不会是均匀的。
2021-02-28 16:31:29
jeff atwood 关于这个算法的非常有趣的帖子。blog.codinghorror.com/the-danger-of-naivete 我想知道为什么它是这样实现的
2021-03-01 16:31:29
第一个答案似乎有一个错误。大约每 15 次运行一次,我会得到一个额外的undefined列。jsfiddle.net/tomasswood/z8zm7
2021-03-03 16:31:29
请注意,这会更改初始数组,它不是一种功能方法。只是把这个留在这里给那些盲目复制这个的人(就像我做的一样)。
2021-03-04 16:31:29