我有一个这样的数组:
var arr1 = ["a", "b", "c", "d"];
我如何随机化/打乱它?
我有一个这样的数组:
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);
其他回答
随机化数组,无重复项
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);
从理论的角度来看,在我看来,最优雅的方法是得到一个介于0和n之间的随机数-并计算从{0,1,…,n!-1}到(0,1、2,…,n-1)的所有置换的一对一映射。只要你能使用一个足够可靠的(伪)随机发生器来获得这样一个数字而没有任何明显的偏差,你就有足够的信息来实现你想要的,而不需要其他几个随机数。
当使用IEEE754双精度浮点数计算时,您可以期望随机生成器提供大约15个小数。既然你有15岁=1307674368000(带13位数字),您可以对最多包含15个元素的数组使用以下函数,并假设最多包含14个元素的阵列不会有明显的偏差。如果您正在处理一个固定大小的问题,需要多次计算该洗牌操作,您可能需要尝试以下代码,因为它只使用Math.random一次(但它涉及多次复制操作),因此可能比其他代码更快。
下面的函数不会被使用,但我还是给出了它;它根据此消息中使用的一对一映射(枚举排列时最自然的映射)返回给定排列(0,1,2,…,n-1)的索引;它打算与多达16个元件一起工作:
function permIndex(p) {
var fact = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800, 87178291200, 1307674368000];
var tail = [];
var i;
if (p.length == 0) return 0;
for(i=1;i<(p.length);i++) {
if (p[i] > p[0]) tail.push(p[i]-1);
else tail.push(p[i]);
}
return p[0] * fact[p.length-1] + permIndex(tail);
}
上一个函数的倒数(您自己的问题需要)如下:;它打算与多达16个元件一起工作;它返回(0,1,2,…,s-1)的n阶排列:
function permNth(n, s) {
var fact = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800, 87178291200, 1307674368000];
var i, j;
var p = [];
var q = [];
for(i=0;i<s;i++) p.push(i);
for(i=s-1; i>=0; i--) {
j = Math.floor(n / fact[i]);
n -= j*fact[i];
q.push(p[j]);
for(;j<i;j++) p[j]=p[j+1];
}
return q;
}
现在,你想要的只是:
function shuffle(p) {
var fact = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800, 87178291200, 1307674368000, 20922789888000];
return permNth(Math.floor(Math.random()*fact[p.length]), p.length).map(
function(i) { return p[i]; });
}
它应该适用于多达16个元素,但有一点理论偏差(尽管从实际角度看不明显);它可以被视为完全可用于15个元件;对于包含少于14个元素的数组,您可以放心地认为绝对没有偏差。
所有其他答案都基于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));
使用ES6功能的现代短内联解决方案:
['a','b','c','d'].map(x => [Math.random(), x]).sort(([a], [b]) => a - b).map(([_, x]) => x);
(出于教育目的)
或者像上面所有的答案,但简而言之。
function shuffle(a) { for (var c, d, b = a.length; 0 !== b;)d = Math.floor(Math.random() * b), b -= 1, c = a[b], a[b] = a[d], a[d] = c; return a }