我有一个这样的数组:

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

我如何随机化/打乱它?


当前回答

我发现这个变体挂在“作者删除”的答案中,重复了这个问题。与其他一些已经获得许多支持的答案不同,这是:

实际上是随机的不到位(因此名称被打乱而不是打乱)此处尚未出现多个变体

这是一个jsfiddle,显示了它的使用情况。

Array.prototype.shuffled = function() {
  return this.map(function(n){ return [Math.random(), n] })
             .sort().map(function(n){ return n[1] });
}

其他回答

对于我们这些不是很有天赋但能接触到洛达什奇迹的人来说,有一种叫做洛达什·舒夫的东西。

使用Ramda的功能解决方案。

const {map, compose, sortBy, prop} = require('ramda')

const shuffle = compose(
  map(prop('v')),
  sortBy(prop('i')),
  map(v => ({v, i: Math.random()}))
)

shuffle([1,2,3,4,5,6,7])

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

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

基准

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

随机推送或取消推送(在开头添加)。

['a', 'b', 'c', 'd'].reduce((acc, el) => {
  Math.random() > 0.5 ? acc.push(el) : acc.unshift(el);
  return acc;
}, []);