我有一个这样的数组:

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

我如何随机化/打乱它?


当前回答

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

//阵列var fruits=[“香蕉”、“橙色”、“苹果”、“芒果”];//随机的,随机的fruits.sort(函数(a,b){return 0.5-Math.random()});//输出console.log(水果);

请参考JavaScript排序数组

其他回答

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

重建整个阵列,逐个将每个元素放在一个随机位置。

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

变量ia=[1,2,3];var it=1000;var f=(a,x,i)=>{a.splice(Math.floor(Math.random()*(i+1)),0,x);返回a};var a=新数组(it).fill(ia).map(x=>x.reduce(f,[]));var r=新数组(ia.length).fill(0).map((x,i)=>a.reduce((i2,x2)=>x2[i]+i2,0)/it)console.log(“这些值应该相当相等:”,r);

使用生成器功能的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(混洗)

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

注意:~~(双颚化符运算符)实际上与正实数的Math.floor()类似。这只是一条捷径。

var shuffle=a=>a.length?a.splice(~~(Math.random()*a.length),1).contat(shuffle(a)):a;console.log(JSON.stringify(shuffle([0,1,2,3,4,5,6,7,8,9]));

编辑:由于使用了.splice(),上面的代码是O(n^2),但我们可以通过交换技巧消除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”);洗牌(arr);console.timeEnd(“shuffle”);

问题是,JS无法与大型递归合作。在这种特殊的情况下,数组大小会受到限制,大约为3000~7000,这取决于浏览器引擎和一些未知的事实。

基准

让我们先看看结果,然后看看下面的每个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万元素到位”;字号:粗体;显示:块;}