1679

我有一个这样的数组:

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

如何随机化/随机播放?

4

64 回答 64

2048

事实上的无偏洗牌算法是Fisher-Yates (aka Knuth) Shuffle

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

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);

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

于 2010-03-15T22:41:10.630 回答
992

这是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 开始),从而跳过最后一个元素。

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


编辑:更新到 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]];
    }
}
于 2012-09-28T20:20:11.327 回答
246

您可以使用 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)
  1. 我们将数组中的每个元素放在一个对象中,并给它一个随机排序键
  2. 我们使用随机键排序
  3. 我们取消映射以获取原始对象

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

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

速度

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

于 2017-10-03T13:16:52.743 回答
186

警告!不推荐
使用这种算法,因为它效率低有很大的偏差;看评论。它留在这里供将来参考,因为这个想法并不罕见。

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

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

于 2013-09-06T04:55:22.863 回答
73

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

来自克里斯托夫:

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;
}
于 2012-04-13T13:59:58.647 回答
72

使用 underscore.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();
于 2013-03-31T05:29:09.653 回答
59

新的!

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

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

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

可读版本(使用原始版本。这更慢,变量无用,如闭包&“;”,代码本身也更短......也许阅读这个如何“缩小”Javascript代码,顺便说一句你不能将以下代码压缩到上面的 javascript minifiers 中。)

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
 }
}
于 2014-09-22T23:21:57.377 回答
56

随机播放数组

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 纯,迭代

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]
    )
}
于 2017-09-11T18:12:18.420 回答
40

编辑:这个答案不正确

请参阅评论和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>

于 2017-04-05T15:38:39.527 回答
24

添加到@Laurens Holsts 答案。这是 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
};
于 2013-04-01T21:23:24.990 回答
21

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

  1. 其实随机
  2. 不就地(因此shuffled名称而不是shuffle
  3. 这里还没有多种变体

这是一个 jsfiddle 显示它在使用中

Array.prototype.shuffled = function() {
  return this.map(function(n){ return [Math.random(), n] })
             .sort().map(function(n){ return n[1] });
}
于 2015-06-25T15:25:25.717 回答
21

使用 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();
于 2015-12-20T04:15:25.377 回答
19
//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是一个可能是正数或负数的随机数,因此排序函数会随机重新排序元素。

于 2019-03-20T14:53:48.460 回答
16
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;
};
于 2013-08-09T15:37:42.667 回答
10

递归解决方案:

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));
};
于 2014-03-26T07:47:48.120 回答
10

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);
  }
}
于 2015-08-04T13:50:48.270 回答
10

基准

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

  • 拼接

  • 流行音乐

  • 到位


拼接很慢

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

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

为了夸大缓慢的效果,我们将在包含一百万个元素的数组上演示这一点。以下脚本将近 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 =>
  Math.floor(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;
}


流行很快

诀窍是不要splice使用超级高效的pop. 为此,代替典型的splice呼叫,您 -

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

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

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 =>
  Math.floor(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 毫秒的时间内低于shuffle一百万个元素-

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

const rand = n =>
  Math.floor(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;
}

于 2021-01-09T00:44:05.500 回答
9

使用 ES6 特性的现代短内联解决方案:

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

(用于教育目的)

于 2018-03-15T18:14:29.827 回答
9

这是最简单的一个,

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

例如,您可以在此处查看

于 2020-04-21T04:14:51.910 回答
8

首先,在这里查看一下 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 所指出的,比较函数是用值而不是索引调用的,这就是为什么你需要使用indexOf. 请注意,此更改使代码不太适合indexOf在 O(n) 时间内运行的较大数组。

于 2015-03-29T20:31:14.300 回答
8

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

下面的代码在利用加密级别的随机化时使用众所周知的Fisher-Yates算法。Web 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));

于 2017-09-23T21:33:39.030 回答
8

一个不改变源数组的洗牌函数

更新:在这里我建议一个相对简单(不是从复杂性的角度来看)和简短的算法,它可以很好地处理小型数组,但是当你处理巨大的数组时,它肯定会比经典的Durstenfeld算法花费更多。您可以在此问题的热门回复之一中找到Durstenfeld

原答案:

如果你不希望你的 shuffle 函数改变源数组,你可以将它复制到一个局部变量,然后用一个简单的 shuffle logic完成剩下的工作。

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;
}

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

如果你真的想要它简短,这就是我能走多远:

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;
}
于 2018-01-13T23:16:03.847 回答
8

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

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

请参考JavaScript 排序数组

于 2018-06-20T06:59:22.587 回答
8

使用Fisher-Yates洗牌算法和 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']
于 2020-07-03T10:05:04.617 回答
7

我们在 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%}

于 2019-06-25T08:29:55.647 回答
6

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;
}
于 2013-10-21T13:20:28.807 回答
6

对 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;
};
于 2017-05-19T13:23:11.340 回答
5

随机化数组

 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; 
于 2015-05-07T07:51:52.983 回答
5

虽然已经建议了许多实现,但我觉得我们可以使用 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)
于 2018-04-01T12:15:07.067 回答
5

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

于 2018-12-21T06:45:59.060 回答
5

我发现这很有用:

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]
于 2021-07-14T15:35:26.190 回答
4

最短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;
}
于 2016-10-17T18:13:49.557 回答
4

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

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);

于 2018-02-09T03:32:20.890 回答
4

我使用这两种方法:

此方法不修改原始数组

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);

于 2021-03-22T21:13:27.287 回答
3
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;
}
于 2014-08-21T06:31:07.890 回答
3

从理论的角度来看,在我看来,最优雅的做法是获取0n!-1之间的单个随机数,并计算从. 只要您可以使用足够可靠的(伪)随机生成器来获得这样的数字而没有任何明显的偏差,那么您就有足够的信息来实现您想要的,而不需要其他几个随机数。{0, 1, …, n!-1}(0, 1, 2, …, n-1)

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

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

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 个元素的数组,您可以放心地认为绝对没有偏差。

于 2017-02-19T15:23:58.383 回答
3

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]));

于 2017-04-14T09:41:30.617 回答
3

只是为了在馅饼中有一根手指。在这里,我提出了 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) 由于使用了,.splice()但我们可以通过交换技巧消除 O(n) 中的拼接和洗牌。

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,具体取决于您的浏览器引擎和一些未知事实。

于 2017-08-30T18:10:58.530 回答
2
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
    }
};

只需将数组传递给函数,然后得到改组后的数组

于 2015-10-21T12:35:02.427 回答
2

考虑将其应用于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;
};
于 2016-06-16T20:34:54.853 回答
2

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

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;
}
于 2016-07-24T03:03:24.077 回答
2

我看到没有人给出一个可以连接而不扩展 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));
}

于 2016-11-12T09:40:19.390 回答
2

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

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

但是不要在生产代码中使用它,它不是最佳的,只能与字符串一起使用。

于 2017-02-22T10:51:00.293 回答
2
// 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];
    })
}
于 2018-01-30T01:54:01.383 回答
2

通过使用shuffle-array模块,您可以随机播放您的数组。这是它的简单代码。

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

console.log(collection);

希望这可以帮助。

于 2018-04-23T13:10:43.863 回答
1

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;
}
于 2014-08-04T01:42:32.757 回答
1

使用 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]));

演示

于 2014-11-19T03:47:21.163 回答
1

我自己写了一个 shuffle 函数。这里的区别是它永远不会重复一个值(检查代码):-

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; }
于 2018-01-29T10:06:33.303 回答
1

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(array[, lo[, hi]]) <>

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

于 2018-08-06T21:35:08.727 回答
1

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

['a', 'b', 'c', 'd'].reduce((acc, el) => {
  Math.random() > 0.5 ? acc.push(el) : acc.unshift(el);
  return acc;
}, []);
于 2019-04-11T13:58:00.937 回答
1

重建整个数组,一个一个地把每个元素放在一个随机的地方。

[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);

于 2019-05-09T18:38:17.483 回答
1
 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);
于 2021-05-30T14:31:41.953 回答
1

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

  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);
    }
于 2021-08-26T20:35:20.540 回答
1

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

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());

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

于 2021-09-03T05:37:40.867 回答
1

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

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 }
于 2021-12-19T18:55:22.067 回答
0

使用递归 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;
};
于 2020-04-02T23:24:25.293 回答
0

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

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;

}

于 2020-09-15T20:35:50.913 回答
0

这里有简单的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)
于 2021-06-19T15:41:32.050 回答
-1

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

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)

如您所见,它不是那么随机!soo... 不要使用这种方法!


如果您多次测试。您会看到玩家 1 获得(编号 1)这么多次!
玩家 6 大部分时间都得到了(第 6 号)!

于 2020-01-18T04:40:54.737 回答
-1

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

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"]
于 2020-09-18T09:52:09.317 回答
-1
//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());
于 2021-05-25T21:29:26.607 回答
-2

$=(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());

于 2016-07-25T14:51:23.303 回答
-2

使用 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])
于 2018-10-03T10:45:10.433 回答
-2
[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
})
于 2019-06-05T16:37:07.153 回答