我有一个这样的数组:

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

我如何随机化/打乱它?


当前回答

使用生成器功能的ES6压缩代码*

这是通过从未屏蔽阵列的副本中随机移除项目,直到没有剩余项目。它使用新的ES6生成器功能。

设arr=[1,2,3,4,5,6,7]函数*洗牌(arr){arr=[…arr];而(arr.length)产生arr.splice(Math.random()*arr.length|0,1)[0]}console.log([…shuffle(arr)])

或者,使用ES6和拼接:

设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(混洗)

或者,ES6索引交换方法:

设arr=[1,2,3,4,5,6,7]let shuffled=arr.reduce((a,c,i,r,j)=>(j=数学随机()*(a.length-i)|0,[a[i],a[j]]=[a[j],a[i]],a),[…arr])console.log(混洗)

其他回答

基准

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


拼接速度慢

在循环中使用拼接或移位的任何解决方案都将非常缓慢。当我们增加阵列的大小时,这一点尤其明显。在天真的算法中,我们-

获取输入数组t中的rand位置i将t[i]添加到输出来自阵列t的拼接位置i

为了夸大缓慢的效果,我们将在一百万个元素的数组上演示这一点。以下脚本大约30秒-

常量洗牌=t=>数组起始(样本(t,t.length))函数*样本(t,n){let r=数组.from(t)而(n>0&&r.length){const i=rand(r.length)//1产量r[i]//2r.拼接(i,1)//3n=n-1}}常量rand=n=>0|数学随机()*n函数交换(t,i,j){设q=t[i]t[i]=t[j]t[j]=q返回t}常量大小=1e6const bigarray=Array.from(数组(大小),(_,i)=>i)console.time(“通过拼接洗牌”)常量结果=无序排列(大数组)console.timeEnd(“通过拼接洗牌”)document.body.textContent=JSON.stringify(结果,null,2)正文::之前{内容:“通过拼接100万个元素”;字号:粗体;显示:块;}


流行音乐很快

诀窍不是拼接,而是使用超高效的pop。为此,代替典型的拼接调用-

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

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

常量洗牌=t=>数组起始(样本(t,t.length))函数*样本(t,n){let r=数组.from(t)而(n>0&&r.length){const i=rand(r.length)//1交换(r,i,r.length-1)//2产量r.pop()//3n=n-1}}常量rand=n=>0|数学随机()*n函数交换(t,i,j){设q=t[i]t[i]=t[j]t[j]=q返回t}常量大小=1e6const bigarray=Array.from(数组(大小),(_,i)=>i)console.time(“通过pop洗牌”)常量结果=无序排列(大数组)console.timeEnd(“通过pop进行洗牌”)document.body.textContent=JSON.stringify(结果,null,2)正文::之前{内容:“100万元素通过流行音乐”;字号:粗体;显示:块;}


甚至更快

上面的两个shuffle实现产生了一个新的输出数组。未修改输入数组。这是我的首选工作方式,但你可以通过原地洗牌来提高速度。

在不到10毫秒内洗牌一百万个元素-

函数洗牌(t){让last=t.length设nwhile(last>0){n=兰特(最后一个)交换(t,n,--last)}}常量rand=n=>0|数学随机()*n函数交换(t,i,j){设q=t[i]t[i]=t[j]t[j]=q返回t}常量大小=1e6const bigarray=Array.from(数组(大小),(_,i)=>i)console.time(“shuffle in place”)洗牌(大数组)console.timeEnd(“shuffle in place”)document.body.textContent=JSON.stringify(bigarray,null,2)正文::之前{内容:“100万元素到位”;字号:粗体;显示:块;}

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

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

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

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

d3.洗牌(array[,lo[,hi]])<>使用Fisher–Yates混洗随机化指定阵列的顺序。

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

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 }