我有一个这样的数组:

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

我如何随机化/打乱它?


当前回答

基准

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

其他回答

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

你可以使用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)
//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是一个随机数,可以是正数或负,所以排序函数会随机地重新排序元素。

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

var shuffle=arr=>{常量重复=(arr,currentIndex)=>{console.log(“什么?”,JSON.stringify(arr))如果(当前索引==0){返回arr;}const randomIndex=数学地板(Math.random()*当前索引);常量swap=arr[currentIndex];arr[currentIndex]=arr[randomIndex];arr[randomIndex]=掉期;返回重复(arr,当前索引-1);}返回递归(arr.map(x=>x),arr.length-1);};var arr=[1,2,3,4,5,[6];console.log(shuffle(arr));控制台日志(arr);

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