我有一个这样的数组:

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

我如何随机化/打乱它?


当前回答

随机化数组,无重复项

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

其他回答

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

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

递归解决方案:

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

函数shuffleArray(数组){//在参数中创建具有给定数组长度的新数组const newArray=array.map(()=>null);//创建一个新数组,其中每个索引都包含索引值const arrayReference=array.map((项,索引)=>索引);//对参数中给定的数组进行迭代array.forEach(随机化);return newArray;函数随机化(项){const randomIndex=getRandomIndex();//替换新数组中的值newArray[arrayReference[randomIndex]]=项;//在数组引用中删除使用的索引arrayReference拼接(randomIndex,1);}//返回介于0和当前数组引用长度之间的数字函数getRandomIndex(){常量最小值=0;const max=arrayReference.length;return Math.floor(Math.random()*(max-min))+min;}}控制台日志(shuffleArray([10,20,30,40,60,70,80,90100]);

这是一个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)。请注意,洗牌是在适当的位置完成的,因此如果您不想修改原始数组,请首先使用.sslice(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]];
    }
}

不改变源数组的shuffle函数

更新:这里我建议使用一种相对简单(不是从复杂性角度)和较短的算法,它可以很好地处理小型阵列,但在处理大型阵列时,它的成本肯定要比经典的Durstenfeld算法高得多。你可以在对这个问题的回答中找到杜斯滕菲尔德。

原答覆:

如果您不希望shuffle函数改变源数组,可以将其复制到本地变量,然后使用简单的shuffle逻辑完成其余操作。

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