如何随机化(洗牌)JavaScript数组?

How to randomize (shuffle) a JavaScript array?

提问人:Ali 提问时间:3/16/2010 最后编辑:TheMasterAli 更新时间:8/30/2023 访问量:1529264

问:

我有一个这样的数组:

var arr1 = ["a", "b", "c", "d"];

我怎样才能随机化/随机化它?

JavaScript 数组随机 洗牌

评论

16赞 aug 12/11/2014
只要把这个扔在这里,你就可以用这个可视化工具可视化随机功能实际上是多么随机 Mike Bostock: bost.ocks.org/mike/shuffle/compare.html
6赞 eozzy 9/28/2016
@Blazemonger jsPref 已经死了。你能在这里发帖哪个是最快的吗?
46赞 yuval.bl 9/27/2018
这个怎么样?arr1.sort(() => (Math.random() > .5) ? 1 : -1);
21赞 SaboSuke 9/4/2021
一个简短的回答是a.sort(() => Math.random() - 0.5)
4赞 yuval.bl 11/12/2021
@TheVee在同一规范上看到上面的几行:“排序顺序是实现定义的,如果......如果 comparefn 不是未定义的,并且不是项目元素的一致比较函数”

答:

2462赞 22 revs, 18 users 35%coolaj86 #1

事实上的无偏洗牌算法是 Fisher-Yates(又名 Knuth)洗牌算法。

你可以在这里看到一个很棒的可视化(以及链接到这个的原始帖子)

function shuffle(array) {
  let currentIndex = array.length,  randomIndex;

  // While there remain elements to shuffle.
  while (currentIndex > 0) {

    // Pick a remaining element.
    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex--;

    // And swap it with the current element.
    [array[currentIndex], array[randomIndex]] = [
      array[randomIndex], array[currentIndex]];
  }

  return array;
}

// Used like so
var arr = [2, 11, 37, 42];
shuffle(arr);
console.log(arr);

有关所用算法的更多信息。

评论

20赞 RobG 6/8/2011
上面的答案跳过元素 0,条件应该是 。此外,测试是多余的,因为如果 while 循环永远不会进入。使用 可以更快地调用 。可以删除 tempitempj,并根据需要将值直接分配给 myArray[i]ji----iif (i==0)...i == 0Math.floor...| 0
66赞 theon 7/20/2012
@RobG上面的实现在功能上是正确的。在 Fisher-Yates 算法中,循环并不意味着为数组中的第一个元素运行。查看维基百科,其中还有其他实现也跳过了第一个元素。另请查看这篇文章,其中讨论了为什么循环不为第一个元素运行很重要。
1赞 ggorlen 7/26/2021
如果要在繁忙的循环中执行解构任务,请务必进行转译 - 分配对象的成本很高。
0赞 nkhil 10/5/2021
@ggorlen 在这种情况下,你所说的转译是什么意思?您能给我们举个例子或进一步的解释吗?
9赞 Sam 3/14/2022
我有点惊讶这是最好的答案。其实有很多事情不对劲......作用域不正确,忽略了简单地使用循环,错误地使用 with,如果传递了一个空数组,则为无限循环,以及参数的修改和返回。for!=!==
75赞 con 4/13/2012 #2

可以(但不应该)将其用作 Array 的原型:

来自ChristopheD:

Array.prototype.shuffle = function() {
  var i = this.length, j, temp;
  if ( i == 0 ) return this;
  while ( --i ) {
     j = Math.floor( Math.random() * ( i + 1 ) );
     temp = this[i];
     this[i] = this[j];
     this[j] = temp;
  }
  return this;
}

评论

0赞 Lukas Liesis 3/15/2022
不要碰原型,除非你真的需要在整个程序中洗牌所有或大部分数组,并且你正在没有人能找到它的岩石下编写这个程序。我看到这个答案是十年前的,也许发生在所有“人,停止扩展原型,这很糟糕”的运动之前。stackoverflow.com/questions/14034180/......
0赞 Got To Figure 5/16/2022
在 while 循环中,当您到达 i 为 0 时,它会变为 false,因此忽略列表中的第一个元素,而只洗牌其余元素......所以第一个元素永远不会被洗牌...... +1 在扩展原型时,在我的情况下使代码更具可读性。
1182赞 Laurens Holst 9/29/2012 #3

下面是 Durstenfeld shuffle 的 JavaScript 实现,它是 Fisher-Yates 的优化版本:

/* Randomize array in-place using Durstenfeld shuffle algorithm */
function shuffleArray(array) {
    for (var i = array.length - 1; i > 0; i--) {
        var j = Math.floor(Math.random() * (i + 1));
        var temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
}

它为每个原始数组元素选择一个随机元素,并将其从下一次抽奖中排除,就像从一副纸牌中随机选择一样。

这种巧妙的排除将选取的元素与当前元素交换,然后从其余元素中选取下一个随机元素,向后循环以获得最佳效率,确保随机选取得到简化(它始终可以从 0 开始),从而跳过最后一个元素。

算法运行时为 。请注意,随机排序是就地完成的,因此如果您不想修改原始数组,请先使用 .slice(0) 复制它。O(n)


编辑:更新到 ES6 / ECMAScript 2015

新的 ES6 允许我们一次分配两个变量。当我们想要交换两个变量的值时,这特别方便,因为我们可以在一行代码中完成。这是使用此功能的相同函数的较短形式。

function shuffleArray(array) {
    for (let i = array.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]];
    }
}

评论

14赞 Marjan Venema 12/19/2016
此答案中的实现有利于数组的低端。发现艰难的方式。array.lengt()'。请参阅在 JavaScript 中生成特定范围内的随机整数?,以获得非常全面的解释。Math.random() should not be multiplied with the loop counter + 1, but with
47赞 user94559 3/11/2017
@MarjanVenema 不确定你是否还在关注这个领域,但这个答案是正确的,你建议的改变实际上会带来偏见。有关此错误的精彩文章,请参阅 blog.codinghorror.com/the-danger-of-naivete
5赞 RedPandaCurios 5/19/2021
重复 user94559 的注释,并引用 en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle 要交换的元素 (j) 介于 0 和当前数组索引 (i) 之间
5赞 IRvanFauziE 2/10/2022
你忘了添加吗?return array;
4赞 AgainPsychoX 2/13/2022
@IRvanFauziE 该函数适用于您传递它的数组。添加只会允许更容易地与其他 .return array
76赞 vn_grv 3/31/2013 #4

使用下划线.js库。该方法适用于这种情况。 下面是该方法的示例:_.shuffle()

var _ = require("underscore");

var arr = [1,2,3,4,5,6];
// Testing _.shuffle
var testShuffle = function () {
  var indexOne = 0;
    var stObj = {
      '0': 0,
      '1': 1,
      '2': 2,
      '3': 3,
      '4': 4,
      '5': 5
    };
    for (var i = 0; i < 1000; i++) {
      arr = _.shuffle(arr);
      indexOne = _.indexOf(arr, 1);
      stObj[indexOne] ++;
    }
    console.log(stObj);
};
testShuffle();
26赞 KingKongFrog 4/2/2013 #5

Holsts 的回答增加了@Laurens。这是 50% 的压缩。

function shuffleArray(d) {
  for (var c = d.length - 1; c > 0; c--) {
    var b = Math.floor(Math.random() * (c + 1));
    var a = d[c];
    d[c] = d[b];
    d[b] = a;
  }
  return d
};

评论

8赞 David Jones 4/5/2013
我们应该鼓励人们使用 _.shuffle,而不是从 stack overflow 中粘贴代码;而且,我们应该劝阻人们不要压缩他们的堆栈溢出答案。这就是 jsmin 的用途。
61赞 Blender 5/5/2013
@DavidJones:为什么我要包含整个 4kb 库来洗牌数组?
2赞 Alex K 10/28/2013
在循环中执行而不是在循环中声明 B 并将其分配给 in 循环是否有效?var b = b =
1赞 Brian 7/12/2014
@Alex:Javascript 实际上并不支持块作用域变量(请参阅为什么 JavaScript 没有块作用域?因此,在循环之外声明 b 可能不会有什么不同。
2赞 user2864740 9/15/2014
@Brian 不会有所作为;在解析源代码时,将发生提升。可能不涉及。
17赞 Tophe 8/9/2013 #6
var shuffle = function(array) {
   temp = [];
   originalLength = array.length;
   for (var i = 0; i < originalLength; i++) {
     temp.push(array.splice(Math.floor(Math.random()*array.length),1));
   }
   return temp;
};

评论

0赞 davidatthepark 5/20/2016
这显然不如 Fisher-Yates 算法最优,但它适用于技术面试吗?
0赞 Charlie Wallace 3/21/2019
@Andrea 由于数组长度在 for 循环中更改,代码已损坏。在上次编辑中,此问题已得到纠正。
0赞 mindplay.dk 4/6/2021
你没有声明你的变量,这使得它们成为全局变量 - 这个函数似乎从输入数组中随机删除元素。
231赞 deadrunk 9/6/2013 #7

警告!
不建议使用此算法,因为它效率低下且存在强烈偏差;请参阅注释。它被留在这里供将来参考,因为这个想法并不罕见。

[1,2,3,4,5,6].sort( () => .5 - Math.random() );

https://javascript.info/array-methods#shuffle-an-array 教程直接解释了这些差异。

评论

212赞 radtad 11/14/2013
投反对票,因为这并不是那么随机。我不知道为什么它有这么多的赞成票。请勿使用此方法。它看起来很漂亮,但并不完全正确。以下是 10,000 次迭代后的结果,说明数组中每个数字命中索引 [0] 的次数(我也可以给出其他结果):1 = 29.19%、2 = 29.53%、3 = 20.06%、4 = 11.91%、5 = 5.99%、6 = 3.32%
31赞 deadrunk 11/21/2013
如果你需要随机化相对较小的数组而不处理加密的东西,这很好。我完全同意,如果你需要更多的随机性,你需要使用更复杂的解决方案。
25赞 Blazemonger 12/17/2013
它也是所有可用方法中效率最低的。
15赞 MatsLindh 9/10/2014
问题是它不是确定性的,这会给出错误的结果(如果 1 > 2 和 2 > 3,则应该给出 1 > 3,但这并不能保证这一点。这将混淆排序,并给出由 @radtad) 注释的结果。
5赞 Raphael C 10/21/2013 #8

Fisher-Yates 的另一个实现,使用严格模式:

function shuffleArray(a) {
    "use strict";
    var i, t, j;
    for (i = a.length - 1; i > 0; i -= 1) {
        t = a[i];
        j = Math.floor(Math.random() * (i + 1));
        a[i] = a[j];
        a[j] = t;
    }
    return a;
}

评论

0赞 shortstuffsushi 9/17/2017
与公认的答案相比,使用严格值的添加提供了什么价值?
0赞 Raphael C 9/19/2017
要了解有关严格模式及其如何影响性能的更多信息,您可以在此处阅读:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
0赞 shortstuffsushi 9/19/2017
嗯,你能指出参考文档中的具体内容吗?其中似乎没有提到“提高性能”,除了顶部的模糊评论,可能会使 js 引擎难以优化。在这种情况下,我不清楚严格使用会改进什么。
0赞 Raphael C 9/20/2017
严格模式已经存在了很长一段时间,并且有足够的阅读量供任何人发表自己的意见,如果他们应该始终使用它以及为什么。例如,Jslint 已经足够清楚地表明,您应该始终使用严格模式。道格拉斯·克罗克福德(Douglas Crockford)写了相当多的文章和一些很棒的视频,讲述了为什么始终使用严格模式很重要,这不仅是一种好的做法,而且浏览器js引擎(如V8)如何以不同的方式解释它。我强烈建议你用谷歌搜索一下,并对此发表自己的意见。
0赞 Raphael C 9/21/2017
这是一个关于严格模式下性能的旧线程,有点旧但仍然相关:stackoverflow.com/questions/3145966/......
13赞 Julian K. 3/26/2014 #9

递归解决方案:

function shuffle(a,b){
    return a.length==0?b:function(c){
        return shuffle(a,(b||[]).concat(c));
    }(a.splice(Math.floor(Math.random()*a.length),1));
};
1赞 Noel Hartsell 8/4/2014 #10

Fisher-Yates 的这种变体效率略高,因为它避免了与自身交换元素:

function shuffle(array) {
  var elementsRemaining = array.length, temp, randomIndex;
  while (elementsRemaining > 1) {
    randomIndex = Math.floor(Math.random() * elementsRemaining--);
    if (randomIndex != elementsRemaining) {
      temp = array[elementsRemaining];
      array[elementsRemaining] = array[randomIndex];
      array[randomIndex] = temp;
    }
  }
  return array;
}
3赞 user1289673 8/21/2014 #11
Array.prototype.shuffle=function(){
   var len = this.length,temp,i
   while(len){
    i=Math.random()*len-- |0;
    temp=this[len],this[len]=this[i],this[i]=temp;
   }
   return this;
}

评论

0赞 Oriol 8/12/2016
要截断,您应该使用 而不是 .数组索引可以高于 2³¹-1。n >>> 0n | 0
68赞 cocco 9/23/2014 #12

新增功能!

更短且可能*更快的 Fisher-Yates 洗牌算法

  1. 它使用 while---
  2. 按位到下限(最多 10 位十进制数字(32 位))
  3. 删除了不必要的闭包和其他东西

function fy(a,b,c,d){//array,placeholder,placeholder,placeholder
 c=a.length;while(c)b=Math.random()*(--c+1)|0,d=a[c],a[c]=a[b],a[b]=d
}

脚本大小(函数名称为 fy):90 字节

演示 http://jsfiddle.net/vvpoma8w/

*可能在所有浏览器上都更快,除了 Chrome。

如果您有任何问题,请提出。

编辑

是的,它更快

性能: http://jsperf.com/fyshuffle

使用票数最高的函数。

编辑有一个超额的计算(不需要 --c+1),没有人注意到

更短(4字节)&更快(测试!

function fy(a,b,c,d){//array,placeholder,placeholder,placeholder
 c=a.length;while(c)b=Math.random()*c--|0,d=a[c],a[c]=a[b],a[b]=d
}

在其他地方缓存然后使用也会略微提高大型阵列的性能。var rnd=Math.randomrnd()

http://jsfiddle.net/vvpoma8w/2/

可读版本(使用原始版本,这比较慢,vars是无用的,就像闭包&“;”一样,代码本身也更短...也许阅读这篇如何“缩小”Javascript 代码,顺便说一句,您无法在像上面这样的 javascript 压缩器中压缩以下代码。

function fisherYates( array ){
 var count = array.length,
     randomnumber,
     temp;
 while( count ){
  randomnumber = Math.random() * count-- | 0;
  temp = array[count];
  array[count] = array[randomnumber];
  array[randomnumber] = temp
 }
}

评论

7赞 cocco 9/23/2014
查看性能...在大多数浏览器上速度提高 2 倍...但是需要更多的 jsperf 测试人员......
11赞 cocco 9/23/2014
JS 是一种接受许多快捷方式和不同编写方式的语言。虽然这里有许多可读性很慢的函数,但我只是想展示如何以更高性能的方式完成它,同时节省一些字节......按位和速记在这里真的被低估了,网络上充斥着错误和缓慢的代码。
0赞 Spig 10/10/2014
不是灌篮高手性能的增加。交换 和 ,我在 OS X 10.9.5 上的 Chrome 37 中一直处于底部(与 ~100k 相比,~20k 操作慢了 81%)和 Safari 7.1,它的速度慢了 ~8%。YMMV,但它并不总是更快。jsperf.com/fyshuffle/3fyshuffle prototypefy
1赞 superluminary 9/24/2021
这在精神上是惊人的,同样令人惊奇。我写JS已经20年了,刚才读到这里学到了一些新技巧。我不确定我是被开悟了还是永远被毁了。
1赞 meandre 11/26/2021
如果你的软件除了洗牌数组之外还能做一些有用的事情,那么微优化就无关紧要了。如果您的软件只是在没有任何其他效果的情况下对阵列进行洗牌,那就是浪费人力时间 🤷 ♂️
1赞 Saravanan Rajaraman 11/19/2014 #13

使用 array.splice() 随机化数组

function shuffleArray(array) {
   var temp = [];
   var len=array.length;
   while(len){
      temp.push(array.splice(Math.floor(Math.random()*array.length),1)[0]);
      len--;
   }
   return temp;
}
//console.log("Here >>> "+shuffleArray([4,2,3,5,8,1,0]));

演示

评论

1赞 trincot 7/21/2016
Tophe一年多前发布的基本相同。
8赞 Milo Wielondek 3/30/2015 #14

首先,请看一下这里,了解 javascript 中不同排序方法的精彩视觉比较。

其次,如果你快速浏览一下上面的链接,你会发现与其他方法相比,排序似乎表现得相对较好,同时实现起来非常容易和快速,如下所示:random order

function shuffle(array) {
  var random = array.map(Math.random);
  array.sort(function(a, b) {
    return random[array.indexOf(a)] - random[array.indexOf(b)];
  });
}

编辑:正如@gregers所指出的,比较函数是用值而不是索引调用的,这就是为什么你需要使用.请注意,此更改使代码不太适合在 O(n) 时间内运行的较大数组。indexOfindexOf

评论

0赞 gregers 3/29/2016
Array.prototype.sort传入两个 as 和 ,而不是索引。所以这段代码不起作用。ab
0赞 Milo Wielondek 5/15/2016
@gregers你是对的,我已经编辑了答案。谢谢。
1赞 1' OR 1 -- 7/19/2016
这不是很随机。根据 sort 的实现,与最高索引旁边的元素相比,最低数组索引处的元素可能需要更多的比较才能获得最高索引。这意味着最低索引处的元素不太可能达到最高索引。
5赞 vickisys 5/7/2015 #15

随机化数组

 var arr = ['apple','cat','Adam','123','Zorro','petunia']; 
 var n = arr.length; var tempArr = [];

 for ( var i = 0; i < n-1; i++ ) {

    // The following line removes one random element from arr 
     // and pushes it onto tempArr 
     tempArr.push(arr.splice(Math.floor(Math.random()*arr.length),1)[0]);
 }

 // Push the remaining item onto tempArr 
 tempArr.push(arr[0]); 
 arr=tempArr; 

评论

0赞 Mohebifar 5/9/2015
不应该有 for n,因为你用的不是-1<<=
24赞 Daniel Martin 6/25/2015 #16

我发现这个变体在这个问题的副本的“被作者删除”的答案中挂出来。与其他一些已经有很多赞成票的答案不同,这是:

  1. 其实是随机的
  2. 不是就地的(因此得名而不是shuffledshuffle)
  3. 此处尚未提供多个变体

这是一个jsfiddle,显示了它的使用

Array.prototype.shuffled = function() {
  return this.map(function(n){ return [Math.random(), n] })
             .sort().map(function(n){ return n[1] });
}

评论

0赞 WiredPrairie 7/14/2015
(我怀疑它被删除了,因为它是一种非常低效的随机化数组的方法,尤其是对于较大的数组......而接受的答案,以及该答案的许多其他克隆就地随机化)。
1赞 Daniel Martin 7/15/2015
是的,但鉴于众所周知的错误答案仍然有一堆选票,至少应该提到一个低效但正确的解决方案。
0赞 Daniel Martin 7/15/2015
[1,2,3,4,5,6].sort(function() { return .5 - Math.random(); });- 它不会给出随机排序,如果你使用它,你最终会感到尴尬:robweir.com/blog/2010/02/microsoft-random-browser-ballot.html
3赞 4castle 11/10/2017
如果您希望排序以数字方式比较值,则需要使用。默认比较器是字典比较器,这意味着它将认为小于 since 小于 。.sort(function(a,b){ return a[0] - b[0]; }).sort()10212
1赞 Daniel Martin 11/10/2017
@4castle 好的,我更新了代码,但我要恢复它:词典顺序和数字顺序之间的区别对于产生的范围内的数字无关紧要。(也就是说,在处理从 0(含)到 1(不含)的数字时,字典顺序与数字顺序相同)Math.random()
10赞 Daniel Patru 8/4/2015 #17

Fisher-Yates 在 javascript 中洗牌。我在这里发布这个是因为与这里的其他答案相比,使用两个实用程序函数(swap 和 randInt)澄清了算法。

function swap(arr, i, j) { 
  // swaps two elements of an array in place
  var temp = arr[i];
  arr[i] = arr[j];
  arr[j] = temp;
}
function randInt(max) { 
  // returns random integer between 0 and max-1 inclusive.
  return Math.floor(Math.random()*max);
}
function shuffle(arr) {
  // For each slot in the array (starting at the end), 
  // pick an element randomly from the unplaced elements and
  // place it in the slot, exchanging places with the 
  // element in the slot. 
  for(var slot = arr.length - 1; slot > 0; slot--){
    var element = randInt(slot+1);
    swap(arr, element, slot);
  }
}
2赞 Mayur Nandane 10/21/2015 #18
var shuffledArray = function(inpArr){
    //inpArr - is input array
    var arrRand = []; //this will give shuffled array
    var arrTempInd = []; // to store shuffled indexes
    var max = inpArr.length;
    var min = 0;
    var tempInd;
    var i = 0;

    do{
        //generate random index between range
        tempInd = Math.floor(Math.random() * (max - min));
        //check if index is already available in array to avoid repetition
        if(arrTempInd.indexOf(tempInd)<0){
            //push character at random index
            arrRand[i] = inpArr[tempInd];
            //push random indexes
            arrTempInd.push(tempInd);
            i++;
        }
    }
    // check if random array length is equal to input array length
    while(arrTempInd.length < max){
        return arrRand; // this will return shuffled Array
    }
};

只需将数组传递给函数,然后得到洗牌数组

23赞 BrunoLM 12/20/2015 #19

在ES2015中,您可以使用以下功能:

Array.prototype.shuffle = function() {
  let m = this.length, i;
  while (m) {
    i = (Math.random() * m--) >>> 0;
    [this[m], this[i]] = [this[i], this[m]]
  }
  return this;
}

用法:

[1, 2, 3, 4, 5, 6, 7].shuffle();

评论

5赞 Oriol 7/24/2016
要截断,您应该使用 而不是 .数组索引可以高于 2³¹-1。n >>> 0~~n
1赞 lukejacksonn 5/11/2017
像这样的解构使得如此干净的实现 +1
2赞 Andre Pastore 6/17/2016 #20

考虑到将其应用于 loco 或新的不可变数组,遵循其他解决方案,以下是建议的实现:

Array.prototype.shuffle = function(local){
  var a = this;
  var newArray = typeof local === "boolean" && local ? this : [];
  for (var i = 0, newIdx, curr, next; i < a.length; i++){
    newIdx = Math.floor(Math.random()*i);
    curr = a[i];
    next = a[newIdx];
    newArray[i] = next;
    newArray[newIdx] = curr;
  }
  return newArray;
};
2赞 SynCap 7/24/2016 #21

罗纳德·费舍尔(Ronald Fisher)和弗兰克·耶茨(Frank Yates)洗牌

ES2015 (ES6) 发布

Array.prototype.shuffle2 = function () {
    this.forEach(
        function (v, i, a) {
            let j = Math.floor(Math.random() * (i + 1));
            [a[i], a[j]] = [a[j], a[i]];
        }
    );
    return this;
}

Jet 优化的 ES2015 (ES6) 发布

Array.prototype.shuffle3 = function () {
    var m = this.length;
    while (m) {
        let i = Math.floor(Math.random() * m--);
        [this[m], this[i]] = [this[i], this[m]];
    }
    return this;
}

评论

3赞 Oriol 7/24/2016
这与 BrunoLM 的答案相同
0赞 SynCap 8/3/2016
也许是,但我不这么认为:1)在我的例子中,有 2 个 - 版本,一个 - 用于完全理解,第二个 - 准备使用优化版本,2) 不使用低级忍者的技巧,3) 适用于 ES6 的任何环境。拜托,不要惹新手,嗯。BrunoLM 更新了答案,现在他的回答器更强大了:在 BrunoLM 的代码中,不使用而是直接使用低级数字操作,所以它要快得多。现在(更新后)它可以在任何 ES6 环境中正常工作,并且强烈建议使用,尤其是对于大型阵列。谢谢你,奥里奥尔。Math
-2赞 Abdennour TOUMI 7/25/2016 #22

$=(m)=>console.log(m);

//----add this method to Array class 
Array.prototype.shuffle=function(){
  return this.sort(()=>.5 - Math.random());
};

$([1,65,87,45,101,33,9].shuffle());
$([1,65,87,45,101,33,9].shuffle());
$([1,65,87,45,101,33,9].shuffle());
$([1,65,87,45,101,33,9].shuffle());
$([1,65,87,45,101,33,9].shuffle());

评论

2赞 Domino 7/26/2016
这是非常糟糕的,因为元素很有可能停留在它们的原始位置附近或几乎不从那里移动。
2赞 Abdennour TOUMI 7/26/2016
如果它坏了,把它链接两次或更多次:array.shuffle().shuffle().shuffle()
5赞 Domino 7/27/2016
重复调用会略微降低获得非常相似结果的概率,但这并不能使它成为真正的随机洗牌。在最坏的情况下,即使对 shuffle 的调用次数无限多,仍然可以给出与我们开始时完全相同的数组。Fisher-Yates算法是一个更好且仍然有效的选择。
5赞 Oriol 8/3/2016
请不要再是同样可怕的答案了。
4赞 Tusko Trush 10/18/2016 #23

最短的功能arrayShuffle

function arrayShuffle(o) {
    for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
    return o;
}

评论

1赞 Mingye Wang 10/19/2016
显然你正在做 Sattolo 而不是 Fisher-Yates(Knuth,公正)。
2赞 Francisco Presencia 11/12/2016 #24

我看到还没有人给出一个可以在不扩展 Array 原型的情况下连接的解决方案(这是一种不好的做法)。使用鲜为人知的方法,我们可以很容易地以一种允许串联的方式进行洗牌:reduce()

var randomsquares = [1, 2, 3, 4, 5, 6, 7].reduce(shuffle).map(n => n*n);

您可能希望传递第二个参数,否则如果您尝试在空数组上执行此操作,它将失败:[]

// Both work. The second one wouldn't have worked as the one above
var randomsquares = [1, 2, 3, 4, 5, 6, 7].reduce(shuffle, []).map(n => n*n);
var randomsquares = [].reduce(shuffle, []).map(n => n*n);

让我们定义为:shuffle

var shuffle = (rand, one, i, orig) => {
  if (i !== 1) return rand;  // Randomize it only once (arr.length > 1)

  // You could use here other random algorithm if you wanted
  for (let i = orig.length; i; i--) {
    let j = Math.floor(Math.random() * i);
    [orig[i - 1], orig[j]] = [orig[j], orig[i - 1]];
  }

  return orig;
}

您可以在 JSFiddle 或此处看到它的实际效果:

var shuffle = (all, one, i, orig) => {
    if (i !== 1) return all;

    // You could use here other random algorithm here
    for (let i = orig.length; i; i--) {
        let j = Math.floor(Math.random() * i);
        [orig[i - 1], orig[j]] = [orig[j], orig[i - 1]];
    }

    return orig;
}

for (var i = 0; i < 5; i++) {
  var randomarray = [1, 2, 3, 4, 5, 6, 7].reduce(shuffle, []);
  console.log(JSON.stringify(randomarray));
}

评论

0赞 Mingye Wang 12/14/2016
看来你交换了太多次。有了你,你完全可以执行一个流式的“由内而外”的Fisher-Yates,它被用作回调。(改编自知乎上的公有领域代码。reduce(acc, el) => { acc.push(el); let i = Math.floor(Math.random() * (acc.length)); [acc[i], acc[acc.length - 1]] = [acc[acc.length - 1], acc[i]]; return acc; }
3赞 Thomas Baruchel 2/19/2017 #25

从理论的角度来看,以我的拙见,最优雅的方法是获得一个介于 0 和 n!-1 之间的随机数,并计算从 . 的所有排列的一对一映射。只要您可以使用足够可靠的(伪)随机生成器来获得这样的数字而没有任何明显的偏差,您就有足够的信息来实现您想要的东西,而无需其他几个随机数。{0, 1, …, n!-1}(0, 1, 2, …, n-1)

当使用IEEE754双精度浮点数进行计算时,您可以期望随机生成器提供大约 15 位小数。由于您有 15!=1,307,674,368,000(13 位数字),因此您可以将以下函数用于包含最多 15 个元素的数组,并假设包含最多 14 个元素的数组不会有明显的偏差。如果您正在处理一个固定大小的问题,需要多次计算此随机操作,您可能需要尝试以下代码,该代码可能比其他代码更快,因为它只使用一次(但是它涉及多个复制操作)。Math.random

以下函数不会使用,但我还是给了它;它根据此消息中使用的一对一映射返回给定排列的索引(枚举映射时最自然的映射);它最多可处理 16 个元素:(0, 1, 2, …, n-1)

function permIndex(p) {
    var fact = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800, 87178291200, 1307674368000];
    var tail = [];
    var i;
    if (p.length == 0) return 0;
    for(i=1;i<(p.length);i++) {
        if (p[i] > p[0]) tail.push(p[i]-1);
        else tail.push(p[i]);
    }
    return p[0] * fact[p.length-1] + permIndex(tail);
}

前一个函数的倒数(您自己的问题需要)如下;它旨在处理多达 16 个元素;它返回 n 阶的排列:(0, 1, 2, …, s-1)

function permNth(n, s) {
    var fact = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800, 87178291200, 1307674368000];
    var i, j;
    var p = [];
    var q = [];
    for(i=0;i<s;i++) p.push(i);
    for(i=s-1; i>=0; i--) {
        j = Math.floor(n / fact[i]);
        n -= j*fact[i];
        q.push(p[j]);
        for(;j<i;j++) p[j]=p[j+1];
    }
    return q;
}

现在,你想要的只是:

function shuffle(p) {
    var fact = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800, 87178291200, 1307674368000, 20922789888000];
    return permNth(Math.floor(Math.random()*fact[p.length]), p.length).map(
            function(i) { return p[i]; });
}

它应该适用于多达 16 个元素,但有一点理论偏差(尽管从实践角度来看并不明显);它可以看作是完全可用于 15 个元素;对于包含少于 14 个元素的数组,您可以放心地认为绝对不会有偏差。

评论

0赞 Gershom Maes 1/25/2018
绝对优雅!
2赞 Cezary Daniel Nowak 2/22/2017 #26

我正在考虑将 oneliner 粘贴到控制台中。所有技巧都给出了错误的结果,这是我的实现:.sort

 ['Bob', 'Amy', 'Joy'].map((person) => `${Math.random().toFixed(10)}${person}`).sort().map((person) => person.substr(12));

但不要在生产代码中使用它,它不是最佳的,只适用于字符串。

评论

0赞 Gustavo Rodrigues 8/16/2017
它适用于任何类型的变量:(但不是最佳的)。array.map(e => [Math.random(), e]).sort((a, b) => a[0] - b[0]).map(e => e[1])
43赞 Kris Selbekk 4/5/2017 #27

编辑:这个答案不正确

请参阅注释和 https://stackoverflow.com/a/18650169/28234。它被留在这里供参考,因为这个想法并不罕见。


对于小型数组,一个非常简单的方法就是这样:

const someArray = [1, 2, 3, 4, 5];

someArray.sort(() => Math.random() - 0.5);

它可能不是很有效,但对于小型数组来说,这很好用。下面是一个示例,因此您可以看到它的随机性(或非随机性),以及它是否适合您的用例。

const resultsEl = document.querySelector('#results');
const buttonEl = document.querySelector('#trigger');

const generateArrayAndRandomize = () => {
  const someArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
  someArray.sort(() => Math.random() - 0.5);
  return someArray;
};

const renderResultsToDom = (results, el) => {
  el.innerHTML = results.join(' ');
};

buttonEl.addEventListener('click', () => renderResultsToDom(generateArrayAndRandomize(), resultsEl));
<h1>Randomize!</h1>
<button id="trigger">Generate</button>
<p id="results">0 1 2 3 4 5 6 7 8 9</p>

评论

0赞 Playdome.io 4/11/2017
不错,但每次都会生成一个完整的随机元素吗?
0赞 Kris Selbekk 4/11/2017
不太确定我是否正确理解了你。这种方法确实会在每次调用排序数组时以随机方式(尽管是伪随机)对数组进行随机排序 - 由于显而易见的原因,它不是一个稳定的排序。
6赞 AlexC 6/23/2017
出于与 stackoverflow.com/a/18650169/28234 中解释的相同原因。这更有可能将早期元素留在数组的开头附近。
7赞 Daniel Griscom 11/4/2017
当您需要加扰数组时,这是一个很棒的、简单的单行代码,但不要太在乎结果在学术上是可证明的随机的。有时,最后几英寸的完美需要比它的价值更多的时间。
1赞 superluminary 3/14/2018
如果这有效,那就太好了,但事实并非如此。由于快速搜索的工作方式,不一致的比较器可能会使数组元素靠近其原始位置。您的阵列不会被打乱。
3赞 Lievno 4/14/2017 #28

function shuffleArray(array) {
        // Create a new array with the length of the given array in the parameters
        const newArray = array.map(() => null);

        // Create a new array where each index contain the index value
        const arrayReference = array.map((item, index) => index);

        // Iterate on the array given in the parameters
        array.forEach(randomize);
        
        return newArray;

        function randomize(item) {
            const randomIndex = getRandomIndex();

            // Replace the value in the new array
            newArray[arrayReference[randomIndex]] = item;
            
            // Remove in the array reference the index used
            arrayReference.splice(randomIndex,1);
        }

        // Return a number between 0 and current array reference length
        function getRandomIndex() {
            const min = 0;
            const max = arrayReference.length;
            return Math.floor(Math.random() * (max - min)) + min;
        }
    }
    
console.log(shuffleArray([10,20,30,40,50,60,70,80,90,100]));

7赞 abumalick 5/19/2017 #29

对 CoolAJ86 的答案进行简单修改,不修改原始数组:

 /**
 * Returns a new array whose contents are a shuffled copy of the original array.
 * @param {Array} The items to shuffle.
 * https://stackoverflow.com/a/2450976/1673761
 * https://stackoverflow.com/a/44071316/1673761
 */
const shuffle = (array) => {
  let currentIndex = array.length;
  let temporaryValue;
  let randomIndex;
  const newArray = array.slice();
  // While there remains elements to shuffle...
  while (currentIndex) {
    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex -= 1;
    // Swap it with the current element.
    temporaryValue = newArray[currentIndex];
    newArray[currentIndex] = newArray[randomIndex];
    newArray[randomIndex] = temporaryValue;
  }
  return newArray;
};
3赞 Redu 8/31/2017 #30

只是为了在馅饼里有一根手指。在这里,我介绍了 Fisher Yates shuffle 的递归实现(我认为)。它提供了均匀的随机性。

注意:(双波浪号运算符)实际上的行为类似于正实数。这只是一条捷径。~~Math.floor()

var shuffle = a => a.length ? a.splice(~~(Math.random()*a.length),1).concat(shuffle(a))
                            : a;

console.log(JSON.stringify(shuffle([0,1,2,3,4,5,6,7,8,9])));

编辑:由于使用了 O(n^2),上面的代码是 O(n^2),但我们可以通过交换技巧消除 O(n) 中的拼接和洗牌。.splice()

var shuffle = (a, l = a.length, r = ~~(Math.random()*l)) => l ? ([a[r],a[l-1]] = [a[l-1],a[r]], shuffle(a, l-1))
                                                              : a;

var arr = Array.from({length:3000}, (_,i) => i);
console.time("shuffle");
shuffle(arr);
console.timeEnd("shuffle");

问题是,JS 不能与大递归合作。在这种特殊情况下,您的数组大小被限制为 3000~7000,具体取决于您的浏览器引擎和一些未知的事实。

66赞 Ben Carp 9/12/2017 #31

随机播放阵列就地

function shuffleArr (array){
    for (var i = array.length - 1; i > 0; i--) {
        var rand = Math.floor(Math.random() * (i + 1));
        [array[i], array[rand]] = [array[rand], array[i]]
    }
}

ES6 Pure,迭代

const getShuffledArr = arr => {
    const newArr = arr.slice()
    for (let i = newArr.length - 1; i > 0; i--) {
        const rand = Math.floor(Math.random() * (i + 1));
        [newArr[i], newArr[rand]] = [newArr[rand], newArr[i]];
    }
    return newArr
};

可靠性和性能测试

此页面上的某些解决方案并不可靠(它们仅部分随机化数组)。其他解决方案的效率要低得多。通过(见下文),我们可以测试数组洗牌函数的可靠性和性能。testShuffleArrayFun

function testShuffleArrayFun(getShuffledArrayFun){
    const arr = [0,1,2,3,4,5,6,7,8,9]

    var countArr = arr.map(el=>{
        return arr.map(
            el=> 0
        )
    }) //   For each possible position in the shuffledArr and for 
       //   each possible value, we'll create a counter. 
    const t0 = performance.now()
    const n = 1000000
    for (var i=0 ; i<n ; i++){
        //   We'll call getShuffledArrayFun n times. 
        //   And for each iteration, we'll increment the counter. 
        var shuffledArr = getShuffledArrayFun(arr)
        shuffledArr.forEach(
            (value,key)=>{countArr[key][value]++}
        )
    }
    const t1 = performance.now()
    console.log(`Count Values in position`)
    console.table(countArr)

    const frequencyArr = countArr.map( positionArr => (
        positionArr.map(  
            count => count/n
        )
    )) 

    console.log("Frequency of value in position")
    console.table(frequencyArr)
    console.log(`total time: ${t1-t0}`)
}

其他解决方案

其他解决方案只是为了好玩。

ES6 纯递归

const getShuffledArr = arr => {
    if (arr.length === 1) {return arr};
    const rand = Math.floor(Math.random() * arr.length);
    return [arr[rand], ...getShuffledArr(arr.filter((_, i) => i != rand))];
};

ES6 Pure 使用 array.map

function getShuffledArr (arr){
    return [...arr].map( (_, i, arrCopy) => {
        var rand = i + ( Math.floor( Math.random() * (arrCopy.length - i) ) );
        [arrCopy[rand], arrCopy[i]] = [arrCopy[i], arrCopy[rand]]
        return arrCopy[i]
    })
}

ES6 Pure 使用 array.reduce

function getShuffledArr (arr){
    return arr.reduce( 
        (newArr, _, i) => {
            var rand = i + ( Math.floor( Math.random() * (newArr.length - i) ) );
            [newArr[rand], newArr[i]] = [newArr[i], newArr[rand]]
            return newArr
        }, [...arr]
    )
}

评论

0赞 sheriffderek 9/12/2017
那么,ES6(ES2015)在哪里呢? ?也许你可以概述一下它是如何工作的。为什么选择向下迭代?[array[i], array[rand]]=[array[rand], array[i]]
0赞 Ben Carp 9/12/2017
@sheriffderek 是的,我使用的 ES6 功能是一次分配两个变量,这允许我们在一行代码中交换两个变量。
0赞 Ben Carp 9/15/2017
感谢@sheriffderek提出了升序算法。升序算法可以在归纳中得到证明。
8赞 Marcin Malinowski 9/24/2017 #32

所有其他答案都基于 Math.random(),它速度很快,但不适合加密级别的随机化。

下面的代码使用众所周知的算法,同时用于加密级别的随机化Fisher-YatesWeb Cryptography API

var d = [1,2,3,4,5,6,7,8,9,10];

function shuffle(a) {
	var x, t, r = new Uint32Array(1);
	for (var i = 0, c = a.length - 1, m = a.length; i < c; i++, m--) {
		crypto.getRandomValues(r);
		x = Math.floor(r / 65536 / 65536 * m) + i;
		t = a [i], a [i] = a [x], a [x] = t;
	}

	return a;
}

console.log(shuffle(d));

446赞 superluminary 10/3/2017 #33

您可以使用map和sort轻松完成此操作:

let unshuffled = ['hello', 'a', 't', 'q', 1, 2, 3, {cats: true}]

let shuffled = unshuffled
    .map(value => ({ value, sort: Math.random() }))
    .sort((a, b) => a.sort - b.sort)
    .map(({ value }) => value)
   
console.log(shuffled)

  1. 我们将数组中的每个元素放在一个对象中,并给它一个随机排序键
  2. 我们使用随机键进行排序
  3. 我们取消映射以获取原始对象

你可以对多态数组进行随机排序,排序方式与 Math.random 一样随机,这对于大多数用途来说已经足够了。

由于元素是针对每次迭代都不会重新生成的一致键进行排序的,并且每次比较都从同一分布中提取,因此 Math.random 分布中的任何非随机性都会被抵消。

速度

时间复杂度为 O(N log N),与快速排序相同。空间复杂度为 O(N)。这不如 Fischer Yates 洗牌有效,但在我看来,代码明显更短且功能更强大。如果你有一个大数组,你当然应该使用Fischer Yates。如果你有一个包含几百个项目的小数组,你可以这样做。

评论

25赞 Mark Grimes 6/29/2018
很好。这是 js 中的 Schwartzian 变换
18赞 Software Engineer 9/1/2021
这是这里的最佳答案(对于短数组),原因有很多。对我来说,它真的很有用,因为我在 2021 年使用 react,它最适合这样的函数式方法。
2赞 Ilja KO 3/23/2022
再想想一致性,如果你必须映射 2 次,它已经遍历了元素 N 两次,这还没有考虑到 JS 算法的快速排序复杂性.sort
1赞 random_0620 4/24/2022
@IljaKO 2N 仍为 O(N),小于 O(N log N) 的时间复杂度
9赞 superluminary 6/10/2022
@IljaKO - O(2N + 2(N log N)) 简化为 O(N log N),所以这实际上是 O(N log N)。大 O 符号是关于最大比例因子的。我们删除常量,因为它们不随输入大小缩放,并简化为最大的单个缩放因子。大 O 符号故意不全是关于细节的。
9赞 Evgeniya Manolova 1/14/2018 #34

不更改源数组的 shuffle 函数

免責聲明

请注意,此解决方案不适用于大型阵列!如果要对大型数据集进行洗牌,则应使用上面建议的 Durstenfeld 算法。

溶液

function shuffle(array) {
  const result = [], itemsLeft = array.concat([]);

  while (itemsLeft.length) {
    const randomIndex = Math.floor(Math.random() * itemsLeft.length);
    const [randomItem] = itemsLeft.splice(randomIndex, 1); // take out a random item from itemsLeft
    result.push(randomItem); // ...and add it to the result
  }

  return result;
}

运作方式

  1. 将首字母复制到arrayitemsLeft

  2. 从中获取一个随机索引,将相应的元素添加到数组中并将其从数组中删除itemsLeftresultitemsLeft

  3. 重复步骤(2),直到数组变空itemsLeft

  4. 返回result

评论

0赞 7/9/2018
这本质上是原始的费舍尔-耶茨算法,你是一种非常低效的方式来做他们所谓的“罢工”。如果您不想改变原始数组,则只需复制它,然后使用更高效的 Durstenfeld 变体将该副本随机播放。splice
0赞 Evgeniya Manolova 7/20/2018
@torazaburo,感谢您的反馈。我已经更新了我的答案,以明确表示我宁愿提供一个漂亮的解决方案,而不是一个超大规模的解决方案
0赞 tg_so 4/21/2019
我们也可以使用该方法创建副本,如下所示: .splicesource = array.slice();
0赞 Flo rian 4/16/2023
array.concat([]) 和 [...阵列]
0赞 Evgeniya Manolova 4/20/2023
嗨,弗洛里安,也许你可以在这个线程中找到一个体面的答案:stackoverflow.com/questions/48865710/......
1赞 Saksham Khurana 1/29/2018 #35

我自己写了一个随机播放函数。这里的区别在于它永远不会重复一个值(为此检查代码):-

function shuffleArray(array) {
 var newArray = [];
 for (var i = 0; i < array.length; i++) {
     newArray.push(-1);
 }

 for (var j = 0; j < array.length; j++) {
    var id = Math.floor((Math.random() * array.length));
    while (newArray[id] !== -1) {
        id = Math.floor((Math.random() * array.length));
    }

    newArray.splice(id, 1, array[j]);
 }
 return newArray; }
2赞 Mudlabs 1/30/2018 #36
// Create a places array which holds the index for each item in the
// passed in array.
// 
// Then return a new array by randomly selecting items from the
// passed in array by referencing the places array item. Removing that
// places item each time though.
function shuffle(array) {
    let places = array.map((item, index) => index);
    return array.map((item, index, array) => {
      const random_index = Math.floor(Math.random() * places.length);
      const places_value = places[random_index];
      places.splice(random_index, 1);
      return array[places_value];
    })
}
4赞 HMR 2/9/2018 #37

有趣的是,没有非变异的递归答案:

var shuffle = arr => {
  const recur = (arr,currentIndex)=>{
    console.log("What?",JSON.stringify(arr))
    if(currentIndex===0){
      return arr;
    }
    const randomIndex = Math.floor(Math.random() * currentIndex);
    const swap = arr[currentIndex];
    arr[currentIndex] = arr[randomIndex];
    arr[randomIndex] = swap;
    return recur(
      arr,
      currentIndex - 1
    );
  }
  return recur(arr.map(x=>x),arr.length-1);
};

var arr = [1,2,3,4,5,[6]];
console.log(shuffle(arr));
console.log(arr);

评论

3赞 Bergi 2/9/2018
也许没有,因为它效率很低?9-3
0赞 HMR 2/9/2018
@Bergi 正确,更新了第一个答案逻辑。仍然需要复制数组以实现不可变性。之所以添加,是因为这被标记为一个问题的重复项,该问题要求一个函数接受一个数组并返回一个无序数组而不改变数组。现在这个问题实际上有了OP正在寻找的答案。
11赞 icl7126 3/16/2018 #38

使用 ES6 的现代短直插式解决方案具有以下特点:

['a','b','c','d'].map(x => [Math.random(), x]).sort(([a], [b]) => a - b).map(([_, x]) => x);

(用于教育目的)

评论

0赞 chovy 11/27/2021
这个的分布情况如何?
1赞 icl7126 11/27/2021
@chovy 为了解释正在发生的事情,我们为数组中的每个项目生成随机数,然后按该数字对项目进行排序。因此,只要您从函数中获取“真实随机”数字,您就会得到均匀分布(每个项目在任何位置都有相同的机会)。Math.random()
5赞 Hafizur Rahman 4/1/2018 #39

虽然已经建议了许多实现,但我觉得我们可以使用 forEach 循环使其更短、更容易,因此我们无需担心计算数组长度,而且我们可以安全地避免使用临时变量。

var myArr = ["a", "b", "c", "d"];

myArr.forEach((val, key) => {
  randomIndex = Math.ceil(Math.random()*(key + 1));
  myArr[key] = myArr[randomIndex];
  myArr[randomIndex] = val;
});
// see the values
console.log('Shuffled Array: ', myArr)
2赞 Syed Ayesha Bebe 4/23/2018 #40

通过使用 shuffle-array 模块,您可以对数组进行随机排序。这是它的一个简单的代码。

var shuffle = require('shuffle-array'),
 //collection = [1,2,3,4,5];
collection = ["a","b","c","d","e"];
shuffle(collection);

console.log(collection);

希望这会有所帮助。

6赞 Tính Ngô Quang 6/20/2018 #41

您可以通过以下方式轻松完成:

// array
var fruits = ["Banana", "Orange", "Apple", "Mango"];
// random
fruits.sort(function(a, b){return 0.5 - Math.random()});
// out
console.log(fruits);

请参考 JavaScript 排序数组

评论

0赞 7/8/2018
这种算法早已被证明是有缺陷的。
0赞 Tính Ngô Quang 7/9/2018
请向我证明。我基于 w3schools
7赞 7/9/2018
您可以在 css-tricks.com/snippets/javascript/shuffle-arraynews.ycombinator.com/item?id=2728914 阅读该线程。W3schools一直是一个可怕的信息来源,并且仍然是一个可怕的信息来源。
0赞 Charlie Wallace 3/21/2019
有关为什么这不是一种好方法的良好讨论,请参阅 stackoverflow.com/questions/962802/...
1赞 Xavier Guihot 8/7/2018 #42

d3.js 提供了 Fisher-Yates shuffle内置版本:

console.log(d3.shuffle(["a", "b", "c", "d"]));
<script src="http://d3js.org/d3.v5.min.js"></script>

d3.shuffle(数组[, lo[, hi]])<>

使用 Fisher-Yates 随机排序随机化指定数组的顺序。

-2赞 thomas-peter 10/3/2018 #43

使用 Ramda 的功能解决方案。

const {map, compose, sortBy, prop} = require('ramda')

const shuffle = compose(
  map(prop('v')),
  sortBy(prop('i')),
  map(v => ({v, i: Math.random()}))
)

shuffle([1,2,3,4,5,6,7])
7赞 iPhoney 12/21/2018 #44

对于我们这些不是很有天赋但可以接触到 lodash 奇迹的人来说,有 lodash.shuffle 这样的东西。

20赞 hakki 3/20/2019 #45
//one line solution
shuffle = (array) => array.sort(() => Math.random() - 0.5);


//Demo
let arr = [1, 2, 3];
shuffle(arr);
alert(arr);

https://javascript.info/task/shuffle

Math.random() - 0.5是一个随机数,可以是正数或 负数,因此排序函数会随机对元素进行重新排序。

评论

1赞 trincot 9/29/2020
这不会随着均匀概率分布而洗牌。
9赞 trincot 9/29/2020
这也是这个旧答案的重复。而这个更古老的答案:没有必要重复一个糟糕的算法。
2赞 Pawel 4/11/2019 #46

编辑:不要使用它。结果总是会使元素从一开始就更接近中间。谁知道呢,也许这个算法有用,但不能用于完全随机排序。

随机推或取消移位(在开头添加)。

['a', 'b', 'c', 'd'].reduce((acc, el) => {
  Math.random() > 0.5 ? acc.push(el) : acc.unshift(el);
  return acc;
}, []);
1赞 Alex Szücs 5/10/2019 #47

重建整个数组,一个接一个地将每个元素放在一个随机的位置。

[1,2,3].reduce((a,x,i)=>{a.splice(Math.floor(Math.random()*(i+1)),0,x);return a},[])

var ia= [1,2,3];
var it= 1000;
var f = (a,x,i)=>{a.splice(Math.floor(Math.random()*(i+1)),0,x);return a};
var a = new Array(it).fill(ia).map(x=>x.reduce(f,[]));
var r = new Array(ia.length).fill(0).map((x,i)=>a.reduce((i2,x2)=>x2[i]+i2,0)/it)

console.log("These values should be quite equal:",r);

评论

3赞 ricks 5/10/2019
你应该解释你的代码在做什么,有些人可能不理解这种复杂性的 1 行。
0赞 Sam Mason 5/10/2019
另请注意,由于使用这个是有偏见的,你想做Math.round(... * i)Math.floor(.. * (i+1))
0赞 Alex Szücs 5/11/2019
@SamMason 获得 .5 的概率为 1:100000000000000000000
1赞 Sam Mason 5/11/2019
如果使用 ,则选择第一个和最后一个索引的概率(即 和 ) 是 ,选择任何其他元素的概率为 (其中 )。这对于短数组来说非常糟糕round0n0.5/n1/nn = a.length
0赞 Alex Szücs 5/11/2019
@SamMason感谢您指出错误,我已经更新了答案并制作了测试器
27赞 Rafi Henig 6/6/2019 #48

警告!
不建议将此答案用于随机化大型数组、密码学或任何其他需要真正随机性的应用程序,因为它存在偏差且效率低下。元素位置只是半随机的,它们往往会更接近其原始位置。请参见 https://stackoverflow.com/a/18650169/28234


您可以使用以下命令任意决定是否返回:1 : -1Math.random

[1, 2, 3, 4].sort(() => (Math.random() > 0.5) ? 1 : -1)

尝试运行以下示例:

const array =  [1, 2, 3, 4];

// Based on the value returned by Math.Random,
// the decision is arbitrarily made whether to return 1 : -1

const shuffeled = array.sort(() => {
  const randomTrueOrFalse = Math.random() > 0.5;
  return randomTrueOrFalse ? 1 : -1
});

console.log(shuffeled);

评论

0赞 Juzer Ali 6/21/2019
这是公正的吗?
0赞 user151496 11/2/2019
什么?这太没有意义了。它几乎有 0 几率保持元素完好无损(随机生成正好 0.5)
0赞 Ben Carp 10/10/2022
我建议删除这个答案。答案不正确,也不新鲜。另一个错误的答案留待将来参考,所以我认为这个可以删除:-)
0赞 Rafi Henig 10/11/2022
@BenCarp 首先,非常感谢您的意见和建议!它们将被审查和考虑(至于你断言我的答案不是新的,我相信它与你提到的不同)
0赞 Ben Carp 10/11/2022
@RafiHenig 与其他答案的区别非常非常小。鉴于两者都不正确,仅此而已,我看不出它的意义。
7赞 Yevhen Horbunkov 6/25/2019 #49

2019 年我们仍在洗牌,所以这是我的方法,对我来说似乎很简洁和快速

const src = [...'abcdefg'];

const shuffle = arr => 
  [...arr].reduceRight((res,_,__,s) => 
    (res.push(s.splice(0|Math.random()*s.length,1)[0]), res),[]);

console.log(shuffle(src));
.as-console-wrapper {min-height: 100%}

0赞 user11748403 1/18/2020 #50

社区说不是 100% 随机的!
是的!我测试过,建议不要使用这种方法!
arr.sort((a, b) => 0.5 - Math.random())

let arr = [1, 2, 3, 4, 5, 6]
arr.sort((a, b) => 0.5 - Math.random());

但我不确定。所以我写了一些代码来测试...您也可以尝试!如果你足够感兴趣!

let data_base = []; 
for (let i = 1; i <= 100; i++) { // push 100 time new rendom arr to data_base!
  data_base.push(
    [1, 2, 3, 4, 5, 6].sort((a, b) => {
      return  Math.random() - 0.5;     // used community banned method!  :-)      
    })
  );
} // console.log(data_base);  // if you want to see data!
let analysis = {};
for (let i = 1; i <= 6; i++) {
  analysis[i] = Array(6).fill(0);
}
for (let num = 0; num < 6; num++) {
  for (let i = 1; i <= 100; i++) {
    let plus = data_base[i - 1][num];
    analysis[`${num + 1}`][plus-1]++;
  }
}
console.log(analysis); // analysed result 

在 100 个不同的随机数组中。(我的分析结果)

{ player> 1   2   3  4   5   6
   '1': [ 36, 12, 17, 16, 9, 10 ],
   '2': [ 15, 36, 12, 18, 7, 12 ],
   '3': [ 11, 8, 22, 19, 17, 23 ],
   '4': [ 9, 14, 19, 18, 22, 18 ],
   '5': [ 12, 19, 15, 18, 23, 13 ],
   '6': [ 17, 11, 15, 11, 22, 24 ]
}  
// player 1 got > 1(36 times),2(15 times),...,6(17 times)
// ... 
// ...
// player 6 got > 1(10 times),2(12 times),...,6(24 times)

正如你所看到的,它不是那么随机!所以。。。不要使用这种方法!


如果您多次测试。你会看到玩家 1 得到(数字 1)很多次!
而玩家 6 大部分时间都得到了(数字 6)!

0赞 Stiakov 4/3/2020 #51

使用递归 JS 洗牌数组。

不是最好的实现,但它是递归的,尊重不变性。

const randomizer = (array, output = []) => {
    const arrayCopy = [...array];
    if (arrayCopy.length > 0) {    
        const idx = Math.floor(Math.random() * arrayCopy.length);
        const select = arrayCopy.splice(idx, 1);
        output.push(select[0]);
        randomizer(arrayCopy, output);
    }
    return output;
};
7赞 Aljohn Yamaro 4/21/2020 #52

这是最简单的一个,

function shuffle(array) {
  return array.sort(() => Math.random() - 0.5);
}

有关进一步的示例,您可以在此处查看

评论

2赞 Heretic Monkey 9/24/2020
看起来很像这个旧的答案......
4赞 trincot 9/29/2020
更不用说这个答案了。无需重复...此外,该方法不提供均匀的概率分布。
0赞 Kerwin Sneijders 8/16/2022
从链接中您自己添加:“但是由于排序函数不打算以这种方式使用,因此并非所有排列都具有相同的概率。
10赞 Erik Martín Jordán 7/3/2020 #53

使用 Fisher-Yates shuffle 算法和 ES6:

// Original array
let array = ['a', 'b', 'c', 'd'];

// Create a copy of the original array to be randomized
let shuffle = [...array];

// Defining function returning random value from i to N
const getRandomValue = (i, N) => Math.floor(Math.random() * (N - i) + i);

// Shuffle a pair of two elements at random position j
shuffle.forEach( (elem, i, arr, j = getRandomValue(i, arr.length)) => [arr[i], arr[j]] = [arr[j], arr[i]] );

console.log(shuffle);
// ['d', 'a', 'b', 'c']

评论

0赞 Felipe Augusto 1/9/2021
很棒,易于理解。
0赞 Bruno de Moraes 9/16/2020 #54

我喜欢分享解决这个问题的一百万种方法之一=)

function shuffleArray(array = ["banana", "ovo", "salsicha", "goiaba", "chocolate"]) {
const newArray = [];
let number = Math.floor(Math.random() * array.length);
let count = 1;
newArray.push(array[number]);

while (count < array.length) {
    const newNumber = Math.floor(Math.random() * array.length);
    if (!newArray.includes(array[newNumber])) {
        count++;
        number = newNumber;
        newArray.push(array[number]);
    }
}

return newArray;

}

评论

0赞 Scott Sauyet 1/21/2021
你有没有用一百万个元素尝试过这个?
0赞 Scott Sauyet 1/23/2021
我希望这是.这就是我问的原因。O (n ^ 2)
1赞 Bruno de Moraes 2/19/2021
我为一个小系列制作了它,所以我不担心它。可以肯定的是,我得到的收藏品最多有 20 件。很好的观察!
0赞 Scott Sauyet 2/19/2021
是的,总是存在一个问题,即何时进行任何优化。通常,在处理少量数据时,这很愚蠢。但是这里的几个答案已经发布了最常见的有效洗牌(Fischer-Yates)的变体,它们并不比这复杂得多。我并不是说这里有什么问题,只是您可能希望避免大型数组出现这种情况。
-1赞 Tejas Savaliya 9/18/2020 #55

使用排序方法和数学方法:

var arr =  ["HORSE", "TIGER", "DOG", "CAT"];
function shuffleArray(arr){
  return arr.sort( () => Math.floor(Math.random() * Math.floor(3)) - 1)  
}

// every time it gives random sequence
shuffleArr(arr);
// ["DOG", "CAT", "TIGER", "HORSE"]
// ["HORSE", "TIGER", "CAT", "DOG"]
// ["TIGER", "HORSE", "CAT", "DOG"]

评论

0赞 CiriousJoker 10/17/2020
这不是随机的。请参阅有关在 sort() 中使用 random() 的类似答案的其他评论。
25赞 Mulan 1/9/2021 #56

基准

让我们先看看结果,然后我们将看看下面的每个实现 -shuffle

  • splice

  • pop

  • inplace


拼接速度慢

任何使用或循环的解决方案都会非常慢。当我们增加数组的大小时,这一点尤其明显。在一个幼稚的算法中,我们——spliceshift

  1. 在输入数组中获取一个位置, ,randit
  2. 添加到输出t[i]
  3. splice数组中的位置it

为了夸大慢效应,我们将在包含 100 万个元素的数组上演示这一点。下面的脚本差不多 30 秒 -

const shuffle = t =>
  Array.from(sample(t, t.length))

function* sample(t, n)
{ let r = Array.from(t)
  while (n > 0 && r.length)
  { const i = rand(r.length) // 1
    yield r[i]               // 2
    r.splice(i, 1)           // 3
    n = n - 1
  }
}

const rand = n =>
  0 | Math.random() * n

function swap (t, i, j)
{ let q = t[i]
  t[i] = t[j]
  t[j] = q
  return t
}

const size = 1e6
const bigarray = Array.from(Array(size), (_,i) => i)
console.time("shuffle via splice")
const result = shuffle(bigarray)
console.timeEnd("shuffle via splice")
document.body.textContent = JSON.stringify(result, null, 2)
body::before {
  content: "1 million elements via splice";
  font-weight: bold;
  display: block;
}


流行音乐很快

诀窍不是使用,而是使用超高效。为此,代替典型的呼叫,您 -splicepopsplice

  1. 选择要拼接的位置,i
  2. 与最后一个元素交换,t[i]t[t.length - 1]
  3. 添加到结果t.pop()

现在,我们可以在不到 100 毫秒的时间内处理 100 万个元素shuffle -

const shuffle = t =>
  Array.from(sample(t, t.length))

function* sample(t, n)
{ let r = Array.from(t)
  while (n > 0 && r.length)
  { const i = rand(r.length) // 1
    swap(r, i, r.length - 1) // 2
    yield r.pop()            // 3
    n = n - 1
  }
}

const rand = n =>
  0 | Math.random() * n

function swap (t, i, j)
{ let q = t[i]
  t[i] = t[j]
  t[j] = q
  return t
}

const size = 1e6
const bigarray = Array.from(Array(size), (_,i) => i)
console.time("shuffle via pop")
const result = shuffle(bigarray)
console.timeEnd("shuffle via pop")
document.body.textContent = JSON.stringify(result, null, 2)
body::before {
  content: "1 million elements via pop";
  font-weight: bold;
  display: block;
}


更快

上述两个实现生成一个新的输出数组。输入数组未修改。这是我首选的工作方式,但是您可以通过就地洗牌来进一步提高速度。shuffle

在不到 10 毫秒的时间内低于 100 万个元素shuffle -

function shuffle (t)
{ let last = t.length
  let n
  while (last > 0)
  { n = rand(last)
    swap(t, n, --last)
  }
}

const rand = n =>
  0 | Math.random() * n

function swap (t, i, j)
{ let q = t[i]
  t[i] = t[j]
  t[j] = q
  return t
}

const size = 1e6
const bigarray = Array.from(Array(size), (_,i) => i)
console.time("shuffle in place")
shuffle(bigarray)
console.timeEnd("shuffle in place")
document.body.textContent = JSON.stringify(bigarray, null, 2)
body::before {
  content: "1 million elements in place";
  font-weight: bold;
  display: block;
}

5赞 Blackjack 3/23/2021 #57

我使用这两种方法:

此方法不修改原始数组

shuffle(array);

function shuffle(arr) {
    var len = arr.length;
    var d = len;
    var array = [];
    var k, i;
    for (i = 0; i < d; i++) {
        k = Math.floor(Math.random() * len);
        array.push(arr[k]);
        arr.splice(k, 1);
        len = arr.length;
    }
    for (i = 0; i < d; i++) {
        arr[i] = array[i];
    }
    return arr;
}

var arr = ["a", "b", "c", "d"];
arr = shuffle(arr);
console.log(arr);

此方法修改原始数组

array.shuffle();

Array.prototype.shuffle = function() {
    var len = this.length;
    var d = len;
    var array = [];
    var k, i;
    for (i = 0; i < d; i++) {
        k = Math.floor(Math.random() * len);
        array.push(this[k]);
        this.splice(k, 1);
        len = this.length;
    }
    for (i = 0; i < d; i++) {
        this[i] = array[i];
    }
}

var arr = ["a", "b", "c", "d"];
arr.shuffle();
console.log(arr);

-1赞 realmag777 5/26/2021 #58
//doesn change array
Array.prototype.shuffle = function () {
    let res = [];
    let copy = [...this];

    while (copy.length > 0) {
        let index = Math.floor(Math.random() * copy.length);
        res.push(copy[index]);
        copy.splice(index, 1);
    }

    return res;
};

let a=[1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(a.shuffle());
1赞 Mohamed Salah 5/30/2021 #59
 const arr = [
  { index: 0, value: "0" },
  { index: 1, value: "1" },
  { index: 2, value: "2" },
  { index: 3, value: "3" },
];
let shuffle = (arr) => {
  let set = new Set();
  while (set.size != arr.length) {
    let rand = Math.floor(Math.random() * arr.length);
    set.add(arr[rand]);
  }
  console.log(set);
};
shuffle(arr);
0赞 user3470138 6/19/2021 #60

这里使用简单的 while 循环

 function ShuffleColor(originalArray) {
        let shuffeledNumbers = [];
        while (shuffeledNumbers.length <= originalArray.length) {
            for (let _ of originalArray) {
                const randomNumb = Math.floor(Math.random() * originalArray.length);
                if (!shuffeledNumbers.includes(originalArray[randomNumb])) {
                    shuffeledNumbers.push(originalArray[randomNumb]);
                }
            }
            if (shuffeledNumbers.length === originalArray.length)
                break;
        }
        return shuffeledNumbers;
    }
const colors = [
    '#000000',
    '#2B8EAD',
    '#333333',
    '#6F98A8',
    '#BFBFBF',
    '#2F454E'
]
ShuffleColor(colors)
7赞 Sunny Vakil 7/14/2021 #61

我发现这很有用:

const shuffle = (array: any[]) => {
    return array.slice().sort(() => Math.random() - 0.5);
  }
        
console.log(shuffle([1,2,3,4,5,6,7,8,9,10]));
// Output: [4, 3, 8, 10, 1, 7, 9, 2, 6, 5]

评论

0赞 wortwart 8/18/2021
请注意,这种方法并不是真正随机洗牌:robweir.com/blog/2010/02/microsoft-random-browser-ballot.html
0赞 Akin Zeman 1/26/2022
测试 [“a”,“b”,“c”] 90% “b” 保持相同的顺序(中间)
1赞 Ali Sharifi Neyestani 8/27/2021 #62

为了获得更大的灵活性,您可以添加另一个参数。在这种情况下,您可以从数组中获取一个随机数组并指定新数组的长度:

  function shuffle(array, len = array.length) {
        for (let i = array.length - 1; i > 0; i--) {
            let j = Math.floor(Math.random() * (i + 1));
            [array[i], array[j]] = [array[j], array[i]];
        }

        return array.slice(0, len);
    }
1赞 Calculamatrise 9/3/2021 #63

我找不到我喜欢的。这是我想出的一个解决方案。我没有使用太多无意义的变量,因为这就是我现在的编码方式。

Array.prototype.shuffle = function() {
    for (let i in this) {
        if (this.hasOwnProperty(i)) {
            let index = Math.floor(Math.random() * i);
            [
                this[i],
                this[index]
            ] = [
                this[index],
                this[i]
            ];
        }
    }

    return this;
}

let arrayA = [
    "item1", "item2", "item3", "item4", "item5"
];

Array.prototype.shuffle = function() {
    for (let i in this) {
        if (this.hasOwnProperty(i)) {
            let index = Math.floor(Math.random() * i);
            [
                this[i],
                this[index]
            ] = [
                this[index],
                this[i]
            ];
        }
    }
    
    return this;
}

console.log(arrayA.shuffle());

我希望这对那些可能不太理解这一点的人有所帮助。

1赞 Abrahem haj hle 12/20/2021 #64

或者像上面所有的答案一样,但简而言之。

function shuffle(a) { for (var c, d, b = a.length; 0 !== b;)d = Math.floor(Math.random() * b), b -= 1, c = a[b], a[b] = a[d], a[d] = c; return a }

评论

0赞 shea 1/14/2023
为什么这一切都在一条线上?删除空格不会使代码“变短”
0赞 Abrahem haj hle 1/15/2023
这是一个可选选项,有很多原因需要将简单功能放在一行中。但对我来说:这是因为我写了很多代码,而这种方法似乎将“shuffle”功能放在一行中,使页面中的整个代码更易于阅读。
1赞 Gurami Nikolaishvili 3/10/2022 #65

随机化没有重复项的数组

    function randomize(array){
        let nums = [];
        for(let i = 0; i < array.length; ++i){
            nums.push(i);
        }   
        nums.sort(() => Math.random() - Math.random()).slice(0, array.length)
        for(let i = 0; i < array.length; ++i){
            array[i] = array[nums[i]];
        }
    }
    randomize(array);
2赞 Meow 8/7/2022 #66

为了完整起见,除了 Fischer-Yates 的 Durstenfeld 变体之外,我还要指出 Sattolo 的算法,它只是一个微小的变化,导致每个元素都发生了变化。

function sattoloCycle(arr) {
   for (let i = arr.length - 1; 0 < i; i--) {
      const j = Math.floor(Math.random() * i);
      [arr[i], arr[j]] = [arr[j], arr[i]];
   }
   return arr
}

区别在于随机索引的计算方式,与 .jMath.random() * iMath.random() * (i + 1)

2赞 twizelissa 9/14/2022 #67

洗牌数组元素的可理解方式

 let arr1 = ["a", "b", "c", "d"];
 
function shuffle(array){
let currentIndex = array.length;
while(currentIndex !=0){
let randomIndex = Math.floor(Math.random()*array.length);
currentIndex -=1;
let temp = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex]=temp;  
}
return array;
}
let arr2 = shuffle(arr1);
arr2.forEach(element => console.log(element));

4赞 Mayank Pathela 9/25/2022 #68

您可以使用随机播放。像魅力一样工作lodash

import _ from lodash;

let numeric_array = [2, 4, 6, 9, 10];
let string_array = ['Car', 'Bus', 'Truck', 'Motorcycle', 'Bicycle', 'Person']

let shuffled_num_array = _.shuffle(numeric_array);
let shuffled_string_array = _.shuffle(string_array);

console.log(shuffled_num_array, shuffled_string_array)
5赞 Andrew Parks 1/28/2023 #69

使用生成器函数的 ES6 紧凑代码*

其工作原理是从未洗牌数组的副本中随机删除项目,直到没有剩余项目。它使用新的 ES6 生成器功能。

只要 Math.random() 是公平的,这将是一个完全公平的洗牌

let arr = [1,2,3,4,5,6,7]

function* shuffle(arr) {
  arr = [...arr];
  while(arr.length) yield arr.splice(Math.random()*arr.length|0, 1)[0]
}

console.log([...shuffle(arr)])

或者,使用 ES6 和拼接:

let arr = [1,2,3,4,5,6,7]

let shuffled = arr.reduce(([a,b])=>
  (b.push(...a.splice(Math.random()*a.length|0, 1)), [a,b]),[[...arr],[]])[1]

console.log(shuffled)

或者,ES6 指数互换方法:

let arr = [1,2,3,4,5,6,7]

let shuffled = arr.reduce((a,c,i,r,j)=>
  (j=Math.random()*(a.length-i)|0,[a[i],a[j]]=[a[j],a[i]],a),[...arr])

console.log(shuffled)

1赞 perona chan 5/28/2023 #70

使用和forEachMath.random()

var data = ['a','b','c','d','e']
data.forEach( (value,i) => {
   var random = Math.floor(Math.random() * data.length)
   var tmp = data[random]
   data[random] = value
   data[i] = tmp
})
console.log(data)

评论

0赞 Sambuxc 8/22/2023
我认为随机数有可能相同,因此 data[random] 可能包含重复项。
1赞 kigiri 5/30/2023 #71

我将在这里添加我最常使用的解决方案,因为我没有完全找到这种方法:

const shuffle = array =>
  array
    // Generate a random number for each elements
    .map(value => [Math.random(), value])

    // Sort using each element random number
    .sort(([a], [b]) => a - b)

    // Return back to an array of values
    .map(entry => entry[1])

我喜欢它的是算法的简单性,为每个元素分配一个随机数,然后排序。

我觉得交换有点难以想象,当我必须从无到有时,我可以更有信心地想出这个,我认为它的作用非常清楚,而且我喜欢它没有到位,我的大部分 suffle 都在小数组上,所以这对我的用例非常有用。

我仍然希望我们内置了一个随机播放方法

评论

0赞 Renan Coelho 6/16/2023
由于性能问题,我不建议在生产中运行它。
0赞 kigiri 7/17/2023
这完全取决于你运行此代码的频率,它确实会使用更多的内存,如果它是一个热点,最好总是优化代码
0赞 Renan Coelho 7/17/2023
如果您想要一种更高性能的方式,请尝试以下操作: gist.github.com/sayhicoelho/381d15df6bff074e4c6d8e27d83ee8a8