我有一个这样的数组:

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

我如何随机化/打乱它?


当前回答

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

其他回答

随机化数组,无重复项

    function randomize(array){
        let nums = [];
        for(let i = 0; i < array.length; ++i){
            nums.push(i);
        }   
        nums.sort(() => Math.random() - Math.random()).slice(0, array.length)
        for(let i = 0; i < array.length; ++i){
            array[i] = array[nums[i]];
        }
    }
    randomize(array);

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

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

2019年,我们仍在调整阵列,所以我的方法是:

常量src=[…'abcdefg'];常量shuffle=arr=>[…arr].还原右((res,_,__,s)=>(res.push(s.splice(0|Math.random()*s.length,1)[0]),res),[]);console.log(shuffle(src));.作为控制台包装{最小高度:100%}

基准

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

这是最简单的一个,

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

例如,您可以在此处查看