如何随机化(shuffle)一个JavaScript数组?

How to randomize (shuffle) a JavaScript array?

我有一个这样的数组:

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

我如何随机化/洗牌?


事实上,无偏随机洗算法是fisher-yates(又称knuth)随机洗。

请参阅https://github.com/coolaj86/knuth-shuffle

你可以在这里看到一个很好的可视化效果(和链接到这个的原始帖子)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function shuffle(array) {
  var currentIndex = array.length, temporaryValue, randomIndex;

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

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

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

  return array;
}

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

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


下面是一个Durstenfeld Shuffle的javascript实现,这是Fisher Yates的计算机优化版本:

1
2
3
4
5
6
7
8
9
10
11
12
/**
 * Randomize array element order 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;
    }
}

Fisher-Yates算法的工作原理是为每个原始数组元素选择一个随机元素,然后将其从下一个绘图中排除。就像从一副牌中随机挑选一样。

这种排除是以一种巧妙的方式完成的(由Durstenfeld发明,供计算机使用),方法是将选中的元素与当前元素交换,然后从其余元素中选择下一个随机元素。为了获得最佳的效率,循环向后运行,从而简化随机选取(它可以始终从0开始),并且跳过最后一个元素,因为不再有其他选择。

该算法的运行时间为O(N)。注意,洗牌是在适当的位置进行的。因此,如果不想修改原始数组,请先用.slice(0)复制它。

更新至ES6/ECMAScript 2015

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

1
2
3
4
5
6
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]];
    }
}


[社区编辑:此答案不正确;请参阅评论。它被留在这里供将来参考,因为这个想法并不罕见。]

1
2
3
[1,2,3,4,5,6].sort(function() {
  return .5 - Math.random();
});


可以(或应该)将其用作数组中的原型:

来自克里斯托夫:

1
2
3
4
5
6
7
8
9
10
11
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;
}


使用underline.js库。方法_.shuffle()适用于这种情况。下面是一个方法示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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();


使用map和sort可以很容易地做到这一点:

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

let shuffled = unshuffled
  .map((a) => ({sort: Math.random(), value: a}))
  .sort((a, b) => a.sort - b.sort)
  .map((a) => a.value)
  • 我们将数组中的每个元素放入一个对象中,并给它一个随机排序键
  • 我们用随机键排序
  • 我们取消映射以获取原始对象
  • 你可以对多态数组进行无序排列,排序和math.random一样随机,这对于大多数用途来说已经足够好了。

    由于元素是根据不在每次迭代中重新生成的一致键进行排序的,并且每次比较都是从相同的分布中提取的,所以Math.Random分布中的任何非随机性都会被取消。


    新!

    更短,可能更快的fisher-yates shuffle算法

  • 它使用while---
  • 按位到楼层(数字最多10位十进制数字(32位))
  • 移除了不必要的封口和其他物品
  • 1
    2
    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://jspef.com/fyshuffle

    使用最高投票功能。

    编辑有一个计算超过(不需要——C+1),没有人注意到

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

    1
    2
    3
    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.random,然后使用rnd(),也会稍微提高大型阵列的性能。

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

    可读版本(使用原始版本。这比较慢,var是无用的,就像闭包一样,代码本身也更短…也许读一下如何"缩小"javascript代码,顺便说一句,你不能在像上面这样的javascript浏览器中压缩以下代码。)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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
     }
    }


    小数组的一个非常简单的方法就是:

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

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

    它的效率可能不是很高,但对于小型阵列来说,它工作得很好。这里有一个例子,你可以看到它有多随机(或不随机),以及它是否适合你的用例。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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));
    1
    2
    3
    4
    Randomize!
    <button id="trigger">Generate</button>
    <p id="results">0 1 2 3 4 5 6 7 8 9
    </p>


    添加到@laurens holsts答案中。压缩率为50%。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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
    };


    有些答案可以使用ES6语法缩短。

    ES6纯,迭代

    1
    2
    3
    4
    5
    6
    7
    8
    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
    };

    我个人使用这个功能是因为它是纯的,相对简单的,而且根据我对谷歌Chrome的测试,它是最有效的(与其他纯版本相比)。

    将阵列移动到位

    1
    2
    3
    4
    5
    6
    function getShuffledArr (array){
        for (let i = array.length - 1; i > 0; i--) {
            const rand = Math.floor(Math.random() * (i + 1));
            [array[i], array[rand]] = [array[rand], array[i]]
        }
    }

    可靠性和性能

    正如您在本页中看到的,过去这里提供的解决方案不正确。因此,考虑到可靠性和性能,我编写了以下函数来测试任何纯(无副作用)数组随机化函数。我用它来测试这个答案中的所有选项。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    function testShuffledArrayFun(getShuffledArrayFun){
        const arr = [0,1,2,3,4,5,6,7,8,9]

        let 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 (let i=0 ; i<n ; i++){
            //   We'll call getShuffledArrayFun n times.
            //   And for each iteration, we'll increment the counter.
            const 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}`)
    }

    。typescript-纯数组随机化函数的类型

    您可以使用以下任一项。

    1
    2
    3
    4
    type GetShuffledArr= <T>(arr:Array<T>) => Array<T>
    interface IGetShuffledArr{
        <T>(arr:Array<T>): Array<T>
    }

    其他选项

    ES6纯,递归

    1
    2
    3
    4
    5
    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纯使用array.map

    1
    2
    3
    4
    5
    6
    7
    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纯使用array.reduce

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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]
        )
    }

    这个版本比迭代纯版本效率稍低。


    有了ES2015,您可以使用它:

    1
    2
    3
    4
    5
    6
    7
    8
    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
    [1, 2, 3, 4, 5, 6, 7].shuffle();


    1
    2
    3
    4
    5
    6
    7
    8
    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;
    };


    我在"被作者删除"的答案中找到了这个变体,它是关于这个问题的一个副本。与其他一些已经有很多赞成票的答案不同,这是:

  • 实际上是随机的
  • 没有到位(因此,shuffled名称而不是shuffle名称)
  • 这里还没有多个变体
  • 这里有一个JSfiddle显示它在使用中。

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


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

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

    请在javascript排序数组中引用


    递归解决方案:

    1
    2
    3
    4
    5
    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));
    };

    首先,在这里查看一下不同排序方法在javascript中的视觉比较。

    其次,如果您快速查看上面的链接,您会发现与其他方法相比,random order排序似乎执行得相对较好,同时非常容易和快速地实现,如下所示:

    1
    2
    3
    4
    5
    6
    function shuffle(array) {
      var random = array.map(Math.random);
      array.sort(function(a, b) {
        return random[array.indexOf(a)] - random[array.indexOf(b)];
      });
    }

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


    费希尔·耶茨用JavaScript洗牌。我在这里发布这篇文章是因为使用了两个实用函数(swap和randint)与这里的其他答案相比,它澄清了算法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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);
      }
    }

    不更改源数组的随机播放函数

    更新:这里我建议一个相对简单(不是从复杂性的角度)和短的算法,可以很好地处理小尺寸的数组,但它肯定会比传统的Durstenfeld算法在处理大数组时花费更多。你可以在这个问题的最高级回复中找到德斯坦菲尔德。

    原始答案:

    如果不希望shuffle函数改变源数组,可以将其复制到一个局部变量,然后用一个简单的shuffling逻辑执行其余的操作。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function shuffle(array) {
      var result = [], source = array.concat([]);

      while (source.length) {
        let index = Math.floor(Math.random() * source.length);
        result.push(source[index]);
        source.splice(index, 1);
      }

      return result;
    }

    洗牌逻辑:选取一个随机索引,然后将相应的元素添加到结果数组中,并从源数组副本中删除它。重复此操作,直到源数组变为空。

    如果你真的想要短一点,我可以做到以下几点:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function shuffle(array) {
      var result = [], source = array.concat([]);

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

      return result;
    }


    所有其他答案都是基于math.random()的,它速度快,但不适用于密码学级别的随机化。

    下面的代码使用著名的Fisher-Yates算法,而使用Web Cryptography API进行密码随机化。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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));


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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;
    }


    1
    2
    3
    4
    5
    6
    7
    function shuffle(array) {
      array.sort(() => Math.random() - 0.5);
    }

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

    https://javascript.info/task/shuffle


    只是为了在馅饼里插上一根手指。在这里,我介绍了Fisher Yates Shuffle的递归实现(我认为)。它赋予了统一的随机性。

    注:对于正实数,~~(双颚化符运算符)的行为实际上类似于Math.floor()。只是个捷径而已。

    1
    2
    3
    4
    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])));


    对COOLAJ86答案的简单修改,不修改原始数组:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     /**
     * 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;
    };


    使用ES6功能的现代短内联解决方案:

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

    (用于教育目的)


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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)


    从理论上讲,我认为最优雅的方法是得到一个介于0和n之间的随机数!-1并计算从{0, 1, …, n!-1}(0, 1, 2, …, n-1)的所有排列的一对一映射。只要你能使用一个(伪)随机发生器,它足够可靠,可以得到这样一个没有任何显著偏差的数字,你就有足够的信息来实现你想要的,而不需要其他几个随机数字。

    当使用IEEE754双精度浮点数进行计算时,您可以期望随机生成器提供大约15个小数。既然你有15岁!=1307674368000(有13位数字),您可以使用以下函数与最多包含15个元素的数组一起使用,并假设最多包含14个元素的数组没有明显的偏差。如果您处理的是固定大小的问题,需要多次计算此无序处理操作的次数,那么您可能需要尝试以下代码,因为它只使用一次Math.random,这可能比其他代码更快(但是它涉及多个复制操作)。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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个元素;它返回(0, 1, 2, …, s-1)的n阶排列:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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;
    }

    现在,你只想要:

    1
    2
    3
    4
    5
    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个元素的数组,您可以安全地认为绝对没有偏差。


    有趣的是,没有不可变的递归答案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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);


    最短的arrayShuffle函数

    1
    2
    3
    4
    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;
    }


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     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;


    1
    2
    3
    4
    5
    6
    7
    8
    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;
    }


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 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];
        })
    }


    我看到还没有人给出一个可以连接而不扩展数组原型的解决方案(这是一个糟糕的实践)。使用稍微不太知名的reduce(),我们可以很容易地以允许连接的方式进行洗牌:

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

    您可能希望传递第二个参数[],否则,如果在空数组上尝试这样做,它将失败:

    1
    2
    3
    // 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定义为:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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或此处看到它的作用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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));
    }


    通过使用随机播放阵列模块,您可以随机播放阵列。这是一个简单的代码。

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

    console.log(collection);

    希望这有帮助。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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
        }
    };

    只需将数组传递给函数,然后返回洗牌数组


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    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]));


    我正在考虑在控制台上粘贴一行程序。所有与.sort有关的技巧都给出了错误的结果,下面是我的实现:

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

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


    罗纳德·费舍尔和弗兰克·耶茨洗牌

    ES2015 (ES6) release

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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 optimized ES2015 (ES6) release

    1
    2
    3
    4
    5
    6
    7
    8
    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;
    }


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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;
    };


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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; }


    d3.js提供了fisher-yates shuffle的内置版本:

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

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

    Randomizes the order of the specified array using the Fisher–Yates shuffle.


    对于我们这些不是很有天赋但能接触到罗达什奇观的人来说,有一件事就是罗达什洗牌。


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

    1
    2
    3
    4
    ['a', 'b', 'c', 'd'].reduce((acc, el) => {
      Math.random() > 0.5 ? acc.push(el) : acc.unshift(el);
      return acc;
    }, []);

    重建整个数组,逐个将每个元素放在随机位置。

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

    1
    2
    3
    4
    5
    6
    7
    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);


    Fisher Yates的这种变化稍微更为有效,因为它避免了将一个元素与自身交换:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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;
    }


    1
    2
    3
    4
    5
    [1, 2, 3, 4, 5, 6, 7, 8, 9, 0].sort((x, z) => {
        ren = Math.random();
        if (ren == 0.5) return 0;
        return ren > 0.5 ? 1 : -1
    })

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    $=(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());


    使用Ramda的功能解决方案。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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])

    Shuffle array of strings:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    shuffle = (array) => {
      let counter = array.length, temp, index;
      while ( counter > 0 ) {
        index = Math.floor( Math.random() * counter );
        counter--;
        temp = array[ counter ];
        array[ counter ] = array[ index ];
        array[ index ] = temp;
      }
      return array;
     }