我有一个这样的数组:

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

我如何随机化/打乱它?


当前回答

递归解决方案:

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

其他回答

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

您可以轻松地使用地图和排序:

让unshuffled=['hello','a','t','q',1,2,3,{cats:true}]让洗牌.map(value=>({value,sort:Math.random()})).sort((a,b)=>a.sort-b.sort).map(({value})=>值)console.log(混洗)

我们将数组中的每个元素放在一个对象中,并给它一个随机排序键我们使用随机键排序我们取消映射以获取原始对象

您可以对多态数组进行排序,排序就像Math.random一样随机,这对于大多数目的来说都足够好。

由于元素是根据每次迭代都不会重新生成的一致键进行排序的,并且每次比较都来自相同的分布,因此Math.random分布中的任何非随机性都会被取消。

速度

时间复杂度为O(N log N),与快速排序相同。空间复杂度为0(N)。这不像Fischer Yates洗牌那样高效,但在我看来,代码明显更短,功能更强大。如果你有一个大数组,你当然应该使用Fischer Yates。如果您有一个包含几百个项目的小数组,您可以这样做。

所有其他答案都基于Math.random(),它很快,但不适用于密码级别的随机化。

下面的代码使用了众所周知的Fisher Yates算法,同时利用Web Cryptography API实现了随机化的加密级别。

变量d=[1,2,3,4,5,6,7,8,9,10];函数洗牌(a){var x,t,r=新Uint32Array(1);对于(var i=0,c=a.length-1,m=a.length;i<c;i++,m-){crypto.getRandomValues(r);x=数学楼层(r/65536/65536*m)+i;t=a[i],a[i]=a[x],a[x]=t;}返回a;}console.log(shuffle(d));

最短的arrayShuffle函数

function arrayShuffle(o) {
    for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
    return o;
}

函数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]);