我有一个数组,其形式如下:
var arr1 = ["a", "b", "c", "d"];
我该如何对它进行随机排列 / 洗牌?
我有一个数组,其形式如下:
var arr1 = ["a", "b", "c", "d"];
我该如何对它进行随机排列 / 洗牌?
从理论上讲,我觉得最优雅的方法是获取一个介于0和n!-1之间的单个随机数,并计算从{0,1,…,n!- 1}到所有排列(0,1,2,…,n-1)的一对一映射。只要您可以使用足够可靠的(伪)随机生成器获得这样的数字,而不需要多个其他随机数,就足以实现所需目标。
当使用IEEE754双精度浮点数进行计算时,可以预期随机生成器提供约15个小数位。由于您有15!= 1,307,674,368,000(13位数),因此可以使用以下函数处理包含高达15个元素的数组,并假定包含高达14个元素的数组不会出现重大偏差。如果您在解决需要多次计算此洗牌操作的固定大小的问题,则可能希望尝试以下代码,该代码可能比其他代码更快,因为它仅使用Math.random
一次(但涉及多个复制操作)。
以下函数将不被使用,但我还是提供一下;它根据本消息中使用的一对一映射返回给定排列的索引(在枚举排列时最自然的一种);它旨在处理高达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个元素的数组,您可以安全地认为没有任何偏差。
通过使用shuffle-array模块,您可以随机打乱数组。以下是一个简单的代码示例。
var shuffle = require('shuffle-array'),
//collection = [1,2,3,4,5];
collection = ["a","b","c","d","e"];
shuffle(collection);
console.log(collection);
考虑将其应用于in loco或新的不可变数组,遵循其他解决方案,这里提供了一个建议的实现:
Array.prototype.shuffle = function(local){
var a = this;
var newArray = typeof local === "boolean" && local ? this : [];
for (var i = 0, newIdx, curr, next; i < a.length; i++){
newIdx = Math.floor(Math.random()*i);
curr = a[i];
next = a[newIdx];
newArray[i] = next;
newArray[newIdx] = curr;
}
return newArray;
};
// Create a places array which holds the index for each item in the
// passed in array.
//
// Then return a new array by randomly selecting items from the
// passed in array by referencing the places array item. Removing that
// places item each time though.
function shuffle(array) {
let places = array.map((item, index) => index);
return array.map((item, index, array) => {
const random_index = Math.floor(Math.random() * places.length);
const places_value = places[random_index];
places.splice(random_index, 1);
return array[places_value];
})
}
var shuffledArray = function(inpArr){
//inpArr - is input array
var arrRand = []; //this will give shuffled array
var arrTempInd = []; // to store shuffled indexes
var max = inpArr.length;
var min = 0;
var tempInd;
var i = 0;
do{
//generate random index between range
tempInd = Math.floor(Math.random() * (max - min));
//check if index is already available in array to avoid repetition
if(arrTempInd.indexOf(tempInd)<0){
//push character at random index
arrRand[i] = inpArr[tempInd];
//push random indexes
arrTempInd.push(tempInd);
i++;
}
}
// check if random array length is equal to input array length
while(arrTempInd.length < max){
return arrRand; // this will return shuffled Array
}
};
只需将数组传递给函数,然后返回洗牌后的数组。
罗纳德·费舍尔和弗兰克·耶茨洗牌算法
ES2015(ES6)发布
Array.prototype.shuffle2 = function () {
this.forEach(
function (v, i, a) {
let j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
);
return this;
}
Array.prototype.shuffle3 = function () {
var m = this.length;
while (m) {
let i = Math.floor(Math.random() * m--);
[this[m], this[i]] = [this[i], this[m]];
}
return this;
}
Math
,而是直接使用低级数字操作,因此它要快得多。现在(更新后),它可以在任何ES6环境中正常工作,特别是对于大数组非常推荐。谢谢你,Oriol。 - SynCapfunction sattoloCycle(arr) {
for (let i = arr.length - 1; 0 < i; i--) {
const j = Math.floor(Math.random() * i);
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr
}
j
,使用的是 Math.random() * i
和 Math.random() * (i + 1)
。编辑:不要使用这个。结果总是会使元素从开头到中间更接近。谁知道,也许这个算法有用,但不适用于完全随机排序。
随机地推送或在开头添加。
['a', 'b', 'c', 'd'].reduce((acc, el) => {
Math.random() > 0.5 ? acc.push(el) : acc.unshift(el);
return acc;
}, []);
Understandable way of shuffling elements of an array
let arr1 = ["a", "b", "c", "d"];
function shuffle(array){
let currentIndex = array.length;
while(currentIndex !=0){
let randomIndex = Math.floor(Math.random()*array.length);
currentIndex -=1;
let temp = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex]=temp;
}
return array;
}
let arr2 = shuffle(arr1);
arr2.forEach(element => console.log(element));
我在思考一个可以在控制台中粘贴的一行代码。所有使用.sort
的技巧都会给出错误的结果,这是我的实现:
['Bob', 'Amy', 'Joy'].map((person) => `${Math.random().toFixed(10)}${person}`).sort().map((person) => person.substr(12));
但是不要在生产代码中使用它,因为它不够优化,只能使用字符串。
array.map(e => [Math.random(), e]).sort((a, b) => a[0] - b[0]).map(e => e[1])
(但不是最佳选择)。 - Gustavo Rodrigues
a.sort(() => Math.random() - 0.5)
的意思是将数组 a 随机排序。 - SaboSuke