提问人:Ali 提问时间:3/16/2010 最后编辑:TheMasterAli 更新时间:11/22/2023 访问量:1531117
如何随机化(洗牌)JavaScript数组?
How to randomize (shuffle) a JavaScript array?
答:
事实上的无偏洗牌算法是 Fisher-Yates(又名 Knuth)洗牌算法。
你可以在这里看到一个很棒的可视化(以及链接到这个的原始帖子)
function shuffle(array) {
let currentIndex = array.length, randomIndex;
// While there remain elements to shuffle.
while (currentIndex > 0) {
// Pick a remaining element.
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
// And swap it with the current element.
[array[currentIndex], array[randomIndex]] = [
array[randomIndex], array[currentIndex]];
}
return array;
}
// Used like so
var arr = [2, 11, 37, 42];
shuffle(arr);
console.log(arr);
有关所用算法的更多信息。
评论
i--
--i
if (i==0)...
i == 0
Math.floor
...| 0
for
!=
!==
可以(但不应该)将其用作 Array 的原型:
来自ChristopheD:
Array.prototype.shuffle = function() {
var i = this.length, j, temp;
if ( i == 0 ) return this;
while ( --i ) {
j = Math.floor( Math.random() * ( i + 1 ) );
temp = this[i];
this[i] = this[j];
this[j] = temp;
}
return this;
}
评论
下面是 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 开始),从而跳过最后一个元素。
算法运行时为 。请注意,随机排序是就地完成的,因此如果您不想修改原始数组,请先使用 .slice(0)
复制它。O(n)
编辑:更新到 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]];
}
}
评论
Math.random() should not be multiplied with the loop counter + 1, but with
return array;
return array
使用下划线.js库。该方法适用于这种情况。
下面是该方法的示例:_.shuffle()
var _ = require("underscore");
var arr = [1,2,3,4,5,6];
// Testing _.shuffle
var testShuffle = function () {
var indexOne = 0;
var stObj = {
'0': 0,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5
};
for (var i = 0; i < 1000; i++) {
arr = _.shuffle(arr);
indexOne = _.indexOf(arr, 1);
stObj[indexOne] ++;
}
console.log(stObj);
};
testShuffle();
Holsts 的回答增加了@Laurens。这是 50% 的压缩。
function shuffleArray(d) {
for (var c = d.length - 1; c > 0; c--) {
var b = Math.floor(Math.random() * (c + 1));
var a = d[c];
d[c] = d[b];
d[b] = a;
}
return d
};
评论
var b =
b =
var shuffle = function(array) {
temp = [];
originalLength = array.length;
for (var i = 0; i < originalLength; i++) {
temp.push(array.splice(Math.floor(Math.random()*array.length),1));
}
return temp;
};
评论
警告!
不建议使用此算法,因为它效率低下且存在强烈偏差;请参阅注释。它被留在这里供将来参考,因为这个想法并不罕见。
[1,2,3,4,5,6].sort( () => .5 - Math.random() );
本 https://javascript.info/array-methods#shuffle-an-array 教程直接解释了这些差异。
评论
Fisher-Yates 的另一个实现,使用严格模式:
function shuffleArray(a) {
"use strict";
var i, t, j;
for (i = a.length - 1; i > 0; i -= 1) {
t = a[i];
j = Math.floor(Math.random() * (i + 1));
a[i] = a[j];
a[j] = t;
}
return a;
}
评论
递归解决方案:
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));
};
Fisher-Yates 的这种变体效率略高,因为它避免了与自身交换元素:
function shuffle(array) {
var elementsRemaining = array.length, temp, randomIndex;
while (elementsRemaining > 1) {
randomIndex = Math.floor(Math.random() * elementsRemaining--);
if (randomIndex != elementsRemaining) {
temp = array[elementsRemaining];
array[elementsRemaining] = array[randomIndex];
array[randomIndex] = temp;
}
}
return array;
}
Array.prototype.shuffle=function(){
var len = this.length,temp,i
while(len){
i=Math.random()*len-- |0;
temp=this[len],this[len]=this[i],this[i]=temp;
}
return this;
}
评论
n >>> 0
n | 0
新增功能!
更短且可能*更快的 Fisher-Yates 洗牌算法
- 它使用 while---
- 按位到下限(最多 10 位十进制数字(32 位))
- 删除了不必要的闭包和其他东西
function fy(a,b,c,d){//array,placeholder,placeholder,placeholder
c=a.length;while(c)b=Math.random()*(--c+1)|0,d=a[c],a[c]=a[b],a[b]=d
}
脚本大小(函数名称为 fy):90 字节
演示 http://jsfiddle.net/vvpoma8w/
*可能在所有浏览器上都更快,除了 Chrome。
如果您有任何问题,请提出。
编辑
是的,它更快
性能: http://jsperf.com/fyshuffle
使用票数最高的函数。
编辑有一个超额的计算(不需要 --c+1),没有人注意到
更短(4字节)&更快(测试!
function fy(a,b,c,d){//array,placeholder,placeholder,placeholder
c=a.length;while(c)b=Math.random()*c--|0,d=a[c],a[c]=a[b],a[b]=d
}
在其他地方缓存然后使用也会略微提高大型阵列的性能。var rnd=Math.random
rnd()
http://jsfiddle.net/vvpoma8w/2/
可读版本(使用原始版本,这比较慢,vars是无用的,就像闭包&“;”一样,代码本身也更短...也许阅读这篇如何“缩小”Javascript 代码,顺便说一句,您无法在像上面这样的 javascript 压缩器中压缩以下代码。
function fisherYates( array ){
var count = array.length,
randomnumber,
temp;
while( count ){
randomnumber = Math.random() * count-- | 0;
temp = array[count];
array[count] = array[randomnumber];
array[randomnumber] = temp
}
}
评论
fy
shuffle prototype
fy
使用 array.splice() 随机化数组
function shuffleArray(array) {
var temp = [];
var len=array.length;
while(len){
temp.push(array.splice(Math.floor(Math.random()*array.length),1)[0]);
len--;
}
return temp;
}
//console.log("Here >>> "+shuffleArray([4,2,3,5,8,1,0]));
评论
首先,请看一下这里,了解 javascript 中不同排序方法的精彩视觉比较。
其次,如果你快速浏览一下上面的链接,你会发现与其他方法相比,排序似乎表现得相对较好,同时实现起来非常容易和快速,如下所示:random order
function shuffle(array) {
var random = array.map(Math.random);
array.sort(function(a, b) {
return random[array.indexOf(a)] - random[array.indexOf(b)];
});
}
编辑:正如@gregers所指出的,比较函数是用值而不是索引调用的,这就是为什么你需要使用.请注意,此更改使代码不太适合在 O(n) 时间内运行的较大数组。indexOf
indexOf
评论
Array.prototype.sort
传入两个值 as 和 ,而不是索引。所以这段代码不起作用。a
b
随机化数组
var arr = ['apple','cat','Adam','123','Zorro','petunia'];
var n = arr.length; var tempArr = [];
for ( var i = 0; i < n-1; i++ ) {
// The following line removes one random element from arr
// and pushes it onto tempArr
tempArr.push(arr.splice(Math.floor(Math.random()*arr.length),1)[0]);
}
// Push the remaining item onto tempArr
tempArr.push(arr[0]);
arr=tempArr;
评论
-1
<
<=
我发现这个变体在这个问题的副本的“被作者删除”的答案中挂出来。与其他一些已经有很多赞成票的答案不同,这是:
- 其实是随机的
- 不是就地的(因此得名而不是
shuffled
shuffle
) - 此处尚未提供多个变体
Array.prototype.shuffled = function() {
return this.map(function(n){ return [Math.random(), n] })
.sort().map(function(n){ return n[1] });
}
评论
[1,2,3,4,5,6].sort(function() { return .5 - Math.random(); });
- 它不会给出随机排序,如果你使用它,你最终会感到尴尬:robweir.com/blog/2010/02/microsoft-random-browser-ballot.html
.sort(function(a,b){ return a[0] - b[0]; })
.sort()
10
2
1
2
Math.random()
Fisher-Yates 在 javascript 中洗牌。我在这里发布这个是因为与这里的其他答案相比,使用两个实用程序函数(swap 和 randInt)澄清了算法。
function swap(arr, i, j) {
// swaps two elements of an array in place
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
function randInt(max) {
// returns random integer between 0 and max-1 inclusive.
return Math.floor(Math.random()*max);
}
function shuffle(arr) {
// For each slot in the array (starting at the end),
// pick an element randomly from the unplaced elements and
// place it in the slot, exchanging places with the
// element in the slot.
for(var slot = arr.length - 1; slot > 0; slot--){
var element = randInt(slot+1);
swap(arr, element, slot);
}
}
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中,您可以使用以下功能:
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();
评论
n >>> 0
~~n
考虑到将其应用于 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;
};
罗纳德·费舍尔(Ronald Fisher)和弗兰克·耶茨(Frank Yates)洗牌
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;
}
Jet 优化的 ES2015 (ES6) 发布
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
$=(m)=>console.log(m);
//----add this method to Array class
Array.prototype.shuffle=function(){
return this.sort(()=>.5 - Math.random());
};
$([1,65,87,45,101,33,9].shuffle());
$([1,65,87,45,101,33,9].shuffle());
$([1,65,87,45,101,33,9].shuffle());
$([1,65,87,45,101,33,9].shuffle());
$([1,65,87,45,101,33,9].shuffle());
评论
array.shuffle().shuffle().shuffle()
最短的功能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;
}
评论
我看到还没有人给出一个可以在不扩展 Array 原型的情况下连接的解决方案(这是一种不好的做法)。使用鲜为人知的方法,我们可以很容易地以一种允许串联的方式进行洗牌:reduce()
var randomsquares = [1, 2, 3, 4, 5, 6, 7].reduce(shuffle).map(n => n*n);
您可能希望传递第二个参数,否则如果您尝试在空数组上执行此操作,它将失败:[]
// Both work. The second one wouldn't have worked as the one above
var randomsquares = [1, 2, 3, 4, 5, 6, 7].reduce(shuffle, []).map(n => n*n);
var randomsquares = [].reduce(shuffle, []).map(n => n*n);
让我们定义为:shuffle
var shuffle = (rand, one, i, orig) => {
if (i !== 1) return rand; // Randomize it only once (arr.length > 1)
// You could use here other random algorithm if you wanted
for (let i = orig.length; i; i--) {
let j = Math.floor(Math.random() * i);
[orig[i - 1], orig[j]] = [orig[j], orig[i - 1]];
}
return orig;
}
您可以在 JSFiddle 或此处看到它的实际效果:
var shuffle = (all, one, i, orig) => {
if (i !== 1) return all;
// You could use here other random algorithm here
for (let i = orig.length; i; i--) {
let j = Math.floor(Math.random() * i);
[orig[i - 1], orig[j]] = [orig[j], orig[i - 1]];
}
return orig;
}
for (var i = 0; i < 5; i++) {
var randomarray = [1, 2, 3, 4, 5, 6, 7].reduce(shuffle, []);
console.log(JSON.stringify(randomarray));
}
评论
reduce
(acc, el) => { acc.push(el); let i = Math.floor(Math.random() * (acc.length)); [acc[i], acc[acc.length - 1]] = [acc[acc.length - 1], acc[i]]; return acc; }
从理论的角度来看,以我的拙见,最优雅的方法是获得一个介于 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 个元素:(0, 1, 2, …, n-1)
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 个元素;它返回 n 阶的排列:(0, 1, 2, …, s-1)
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 个元素的数组,您可以放心地认为绝对不会有偏差。
评论
我正在考虑将 oneliner 粘贴到控制台中。所有技巧都给出了错误的结果,这是我的实现:.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])
编辑:这个答案不正确
请参阅注释和 https://stackoverflow.com/a/18650169/28234。它被留在这里供参考,因为这个想法并不罕见。
对于小型数组,一个非常简单的方法就是这样:
const someArray = [1, 2, 3, 4, 5];
someArray.sort(() => Math.random() - 0.5);
它可能不是很有效,但对于小型数组来说,这很好用。下面是一个示例,因此您可以看到它的随机性(或非随机性),以及它是否适合您的用例。
const resultsEl = document.querySelector('#results');
const buttonEl = document.querySelector('#trigger');
const generateArrayAndRandomize = () => {
const someArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
someArray.sort(() => Math.random() - 0.5);
return someArray;
};
const renderResultsToDom = (results, el) => {
el.innerHTML = results.join(' ');
};
buttonEl.addEventListener('click', () => renderResultsToDom(generateArrayAndRandomize(), resultsEl));
<h1>Randomize!</h1>
<button id="trigger">Generate</button>
<p id="results">0 1 2 3 4 5 6 7 8 9</p>
评论
function shuffleArray(array) {
// Create a new array with the length of the given array in the parameters
const newArray = array.map(() => null);
// Create a new array where each index contain the index value
const arrayReference = array.map((item, index) => index);
// Iterate on the array given in the parameters
array.forEach(randomize);
return newArray;
function randomize(item) {
const randomIndex = getRandomIndex();
// Replace the value in the new array
newArray[arrayReference[randomIndex]] = item;
// Remove in the array reference the index used
arrayReference.splice(randomIndex,1);
}
// Return a number between 0 and current array reference length
function getRandomIndex() {
const min = 0;
const max = arrayReference.length;
return Math.floor(Math.random() * (max - min)) + min;
}
}
console.log(shuffleArray([10,20,30,40,50,60,70,80,90,100]));
对 CoolAJ86 的答案进行简单修改,不修改原始数组:
/**
* Returns a new array whose contents are a shuffled copy of the original array.
* @param {Array} The items to shuffle.
* https://stackoverflow.com/a/2450976/1673761
* https://stackoverflow.com/a/44071316/1673761
*/
const shuffle = (array) => {
let currentIndex = array.length;
let temporaryValue;
let randomIndex;
const newArray = array.slice();
// While there remains elements to shuffle...
while (currentIndex) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// Swap it with the current element.
temporaryValue = newArray[currentIndex];
newArray[currentIndex] = newArray[randomIndex];
newArray[randomIndex] = temporaryValue;
}
return newArray;
};
只是为了在馅饼里有一根手指。在这里,我介绍了 Fisher Yates shuffle 的递归实现(我认为)。它提供了均匀的随机性。
注意:(双波浪号运算符)实际上的行为类似于正实数。这只是一条捷径。~~
Math.floor()
var shuffle = a => a.length ? a.splice(~~(Math.random()*a.length),1).concat(shuffle(a))
: a;
console.log(JSON.stringify(shuffle([0,1,2,3,4,5,6,7,8,9])));
编辑:由于使用了 O(n^2),上面的代码是 O(n^2),但我们可以通过交换技巧消除 O(n) 中的拼接和洗牌。.splice()
var shuffle = (a, l = a.length, r = ~~(Math.random()*l)) => l ? ([a[r],a[l-1]] = [a[l-1],a[r]], shuffle(a, l-1))
: a;
var arr = Array.from({length:3000}, (_,i) => i);
console.time("shuffle");
shuffle(arr);
console.timeEnd("shuffle");
问题是,JS 不能与大递归合作。在这种特殊情况下,您的数组大小被限制为 3000~7000,具体取决于您的浏览器引擎和一些未知的事实。
随机播放阵列就地
function shuffleArr (array){
for (var i = array.length - 1; i > 0; i--) {
var rand = Math.floor(Math.random() * (i + 1));
[array[i], array[rand]] = [array[rand], array[i]]
}
}
ES6 Pure,迭代
const getShuffledArr = arr => {
const newArr = arr.slice()
for (let i = newArr.length - 1; i > 0; i--) {
const rand = Math.floor(Math.random() * (i + 1));
[newArr[i], newArr[rand]] = [newArr[rand], newArr[i]];
}
return newArr
};
可靠性和性能测试
此页面上的某些解决方案并不可靠(它们仅部分随机化数组)。其他解决方案的效率要低得多。通过(见下文),我们可以测试数组洗牌函数的可靠性和性能。testShuffleArrayFun
function testShuffleArrayFun(getShuffledArrayFun){
const arr = [0,1,2,3,4,5,6,7,8,9]
var countArr = arr.map(el=>{
return arr.map(
el=> 0
)
}) // For each possible position in the shuffledArr and for
// each possible value, we'll create a counter.
const t0 = performance.now()
const n = 1000000
for (var i=0 ; i<n ; i++){
// We'll call getShuffledArrayFun n times.
// And for each iteration, we'll increment the counter.
var shuffledArr = getShuffledArrayFun(arr)
shuffledArr.forEach(
(value,key)=>{countArr[key][value]++}
)
}
const t1 = performance.now()
console.log(`Count Values in position`)
console.table(countArr)
const frequencyArr = countArr.map( positionArr => (
positionArr.map(
count => count/n
)
))
console.log("Frequency of value in position")
console.table(frequencyArr)
console.log(`total time: ${t1-t0}`)
}
其他解决方案
其他解决方案只是为了好玩。
ES6 纯递归
const getShuffledArr = arr => {
if (arr.length === 1) {return arr};
const rand = Math.floor(Math.random() * arr.length);
return [arr[rand], ...getShuffledArr(arr.filter((_, i) => i != rand))];
};
ES6 Pure 使用 array.map
function getShuffledArr (arr){
return [...arr].map( (_, i, arrCopy) => {
var rand = i + ( Math.floor( Math.random() * (arrCopy.length - i) ) );
[arrCopy[rand], arrCopy[i]] = [arrCopy[i], arrCopy[rand]]
return arrCopy[i]
})
}
ES6 Pure 使用 array.reduce
function getShuffledArr (arr){
return arr.reduce(
(newArr, _, i) => {
var rand = i + ( Math.floor( Math.random() * (newArr.length - i) ) );
[newArr[rand], newArr[i]] = [newArr[i], newArr[rand]]
return newArr
}, [...arr]
)
}
评论
[array[i], array[rand]]=[array[rand], array[i]]
所有其他答案都基于 Math.random(),它速度很快,但不适合加密级别的随机化。
下面的代码使用众所周知的算法,同时用于加密级别的随机化。Fisher-Yates
Web Cryptography API
var d = [1,2,3,4,5,6,7,8,9,10];
function shuffle(a) {
var x, t, r = new Uint32Array(1);
for (var i = 0, c = a.length - 1, m = a.length; i < c; i++, m--) {
crypto.getRandomValues(r);
x = Math.floor(r / 65536 / 65536 * m) + i;
t = a [i], a [i] = a [x], a [x] = t;
}
return a;
}
console.log(shuffle(d));
您可以使用map和sort轻松完成此操作:
let unshuffled = ['hello', 'a', 't', 'q', 1, 2, 3, {cats: true}]
let shuffled = unshuffled
.map(value => ({ value, sort: Math.random() }))
.sort((a, b) => a.sort - b.sort)
.map(({ value }) => value)
console.log(shuffled)
- 我们将数组中的每个元素放在一个对象中,并给它一个随机排序键
- 我们使用随机键进行排序
- 我们取消映射以获取原始对象
你可以对多态数组进行随机排序,排序方式与 Math.random 一样随机,这对于大多数用途来说已经足够了。
由于元素是针对每次迭代都不会重新生成的一致键进行排序的,并且每次比较都从同一分布中提取,因此 Math.random 分布中的任何非随机性都会被抵消。
速度
时间复杂度为 O(N log N),与快速排序相同。空间复杂度为 O(N)。这不如 Fischer Yates 洗牌有效,但在我看来,代码明显更短且功能更强大。如果你有一个大数组,你当然应该使用Fischer Yates。如果你有一个包含几百个项目的小数组,你可以这样做。
评论
.sort
不更改源数组的 shuffle 函数
免責聲明
请注意,此解决方案不适用于大型阵列!如果要对大型数据集进行洗牌,则应使用上面建议的 Durstenfeld 算法。
溶液
function shuffle(array) {
const result = [], itemsLeft = array.concat([]);
while (itemsLeft.length) {
const randomIndex = Math.floor(Math.random() * itemsLeft.length);
const [randomItem] = itemsLeft.splice(randomIndex, 1); // take out a random item from itemsLeft
result.push(randomItem); // ...and add it to the result
}
return result;
}
运作方式
将首字母复制到
array
itemsLeft
从中获取一个随机索引,将相应的元素添加到数组中并将其从数组中删除
itemsLeft
result
itemsLeft
重复步骤(2),直到数组变空
itemsLeft
返回
result
评论
splice
splice
source = array.slice();
我自己写了一个随机播放函数。这里的区别在于它永远不会重复一个值(为此检查代码):-
function shuffleArray(array) {
var newArray = [];
for (var i = 0; i < array.length; i++) {
newArray.push(-1);
}
for (var j = 0; j < array.length; j++) {
var id = Math.floor((Math.random() * array.length));
while (newArray[id] !== -1) {
id = Math.floor((Math.random() * array.length));
}
newArray.splice(id, 1, array[j]);
}
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 shuffle = arr => {
const recur = (arr,currentIndex)=>{
console.log("What?",JSON.stringify(arr))
if(currentIndex===0){
return arr;
}
const randomIndex = Math.floor(Math.random() * currentIndex);
const swap = arr[currentIndex];
arr[currentIndex] = arr[randomIndex];
arr[randomIndex] = swap;
return recur(
arr,
currentIndex - 1
);
}
return recur(arr.map(x=>x),arr.length-1);
};
var arr = [1,2,3,4,5,[6]];
console.log(shuffle(arr));
console.log(arr);
评论
使用 ES6 的现代短直插式解决方案具有以下特点:
['a','b','c','d'].map(x => [Math.random(), x]).sort(([a], [b]) => a - b).map(([_, x]) => x);
(用于教育目的)
评论
Math.random()
虽然已经建议了许多实现,但我觉得我们可以使用 forEach 循环使其更短、更容易,因此我们无需担心计算数组长度,而且我们可以安全地避免使用临时变量。
var myArr = ["a", "b", "c", "d"];
myArr.forEach((val, key) => {
randomIndex = Math.ceil(Math.random()*(key + 1));
myArr[key] = myArr[randomIndex];
myArr[randomIndex] = val;
});
// see the values
console.log('Shuffled Array: ', myArr)
通过使用 shuffle-array 模块,您可以对数组进行随机排序。这是它的一个简单的代码。
var shuffle = require('shuffle-array'),
//collection = [1,2,3,4,5];
collection = ["a","b","c","d","e"];
shuffle(collection);
console.log(collection);
希望这会有所帮助。
您可以通过以下方式轻松完成:
// array
var fruits = ["Banana", "Orange", "Apple", "Mango"];
// random
fruits.sort(function(a, b){return 0.5 - Math.random()});
// out
console.log(fruits);
请参考 JavaScript 排序数组
评论
d3.js 提供了 Fisher-Yates shuffle 的内置版本:
console.log(d3.shuffle(["a", "b", "c", "d"]));
<script src="http://d3js.org/d3.v5.min.js"></script>
d3.shuffle(数组[, lo[, hi]])<>
使用 Fisher-Yates 随机排序随机化指定数组的顺序。
使用 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])
对于我们这些不是很有天赋但可以接触到 lodash 奇迹的人来说,有 lodash.shuffle 这样的东西。
//one line solution
shuffle = (array) => array.sort(() => Math.random() - 0.5);
//Demo
let arr = [1, 2, 3];
shuffle(arr);
alert(arr);
https://javascript.info/task/shuffle
Math.random() - 0.5
是一个随机数,可以是正数或 负数,因此排序函数会随机对元素进行重新排序。
编辑:不要使用它。结果总是会使元素从一开始就更接近中间。谁知道呢,也许这个算法有用,但不能用于完全随机排序。
随机推或取消移位(在开头添加)。
['a', 'b', 'c', 'd'].reduce((acc, el) => {
Math.random() > 0.5 ? acc.push(el) : acc.unshift(el);
return acc;
}, []);
重建整个数组,一个接一个地将每个元素放在一个随机的位置。
[1,2,3].reduce((a,x,i)=>{a.splice(Math.floor(Math.random()*(i+1)),0,x);return a},[])
var ia= [1,2,3];
var it= 1000;
var f = (a,x,i)=>{a.splice(Math.floor(Math.random()*(i+1)),0,x);return a};
var a = new Array(it).fill(ia).map(x=>x.reduce(f,[]));
var r = new Array(ia.length).fill(0).map((x,i)=>a.reduce((i2,x2)=>x2[i]+i2,0)/it)
console.log("These values should be quite equal:",r);
评论
Math.round(... * i)
Math.floor(.. * (i+1))
round
0
n
0.5/n
1/n
n = a.length
警告!
不建议将此答案用于随机化大型数组、密码学或任何其他需要真正随机性的应用程序,因为它存在偏差且效率低下。元素位置只是半随机的,它们往往会更接近其原始位置。请参见 https://stackoverflow.com/a/18650169/28234。
您可以使用以下命令任意决定是否返回:1 : -1
Math.random
[1, 2, 3, 4].sort(() => (Math.random() > 0.5) ? 1 : -1)
尝试运行以下示例:
const array = [1, 2, 3, 4];
// Based on the value returned by Math.Random,
// the decision is arbitrarily made whether to return 1 : -1
const shuffeled = array.sort(() => {
const randomTrueOrFalse = Math.random() > 0.5;
return randomTrueOrFalse ? 1 : -1
});
console.log(shuffeled);
评论
2019 年我们仍在洗牌,所以这是我的方法,对我来说似乎很简洁和快速:
const src = [...'abcdefg'];
const shuffle = arr =>
[...arr].reduceRight((res,_,__,s) =>
(res.push(s.splice(0|Math.random()*s.length,1)[0]), res),[]);
console.log(shuffle(src));
.as-console-wrapper {min-height: 100%}
社区说不是 100% 随机的!
是的!我测试过,建议不要使用这种方法!arr.sort((a, b) => 0.5 - Math.random())
let arr = [1, 2, 3, 4, 5, 6]
arr.sort((a, b) => 0.5 - Math.random());
但我不确定。所以我写了一些代码来测试...您也可以尝试!如果你足够感兴趣!
let data_base = [];
for (let i = 1; i <= 100; i++) { // push 100 time new rendom arr to data_base!
data_base.push(
[1, 2, 3, 4, 5, 6].sort((a, b) => {
return Math.random() - 0.5; // used community banned method! :-)
})
);
} // console.log(data_base); // if you want to see data!
let analysis = {};
for (let i = 1; i <= 6; i++) {
analysis[i] = Array(6).fill(0);
}
for (let num = 0; num < 6; num++) {
for (let i = 1; i <= 100; i++) {
let plus = data_base[i - 1][num];
analysis[`${num + 1}`][plus-1]++;
}
}
console.log(analysis); // analysed result
在 100 个不同的随机数组中。(我的分析结果)
{ player> 1 2 3 4 5 6
'1': [ 36, 12, 17, 16, 9, 10 ],
'2': [ 15, 36, 12, 18, 7, 12 ],
'3': [ 11, 8, 22, 19, 17, 23 ],
'4': [ 9, 14, 19, 18, 22, 18 ],
'5': [ 12, 19, 15, 18, 23, 13 ],
'6': [ 17, 11, 15, 11, 22, 24 ]
}
// player 1 got > 1(36 times),2(15 times),...,6(17 times)
// ...
// ...
// player 6 got > 1(10 times),2(12 times),...,6(24 times)
正如你所看到的,它不是那么随机!所以。。。不要使用这种方法!
如果您多次测试。你会看到玩家 1 得到(数字 1)很多次!
而玩家 6 大部分时间都得到了(数字 6)!
使用递归 JS 洗牌数组。
不是最好的实现,但它是递归的,尊重不变性。
const randomizer = (array, output = []) => {
const arrayCopy = [...array];
if (arrayCopy.length > 0) {
const idx = Math.floor(Math.random() * arrayCopy.length);
const select = arrayCopy.splice(idx, 1);
output.push(select[0]);
randomizer(arrayCopy, output);
}
return output;
};
这是最简单的一个,
function shuffle(array) {
return array.sort(() => Math.random() - 0.5);
}
有关进一步的示例,您可以在此处查看
评论
使用 Fisher-Yates shuffle 算法和 ES6:
// Original array
let array = ['a', 'b', 'c', 'd'];
// Create a copy of the original array to be randomized
let shuffle = [...array];
// Defining function returning random value from i to N
const getRandomValue = (i, N) => Math.floor(Math.random() * (N - i) + i);
// Shuffle a pair of two elements at random position j
shuffle.forEach( (elem, i, arr, j = getRandomValue(i, arr.length)) => [arr[i], arr[j]] = [arr[j], arr[i]] );
console.log(shuffle);
// ['d', 'a', 'b', 'c']
评论
我喜欢分享解决这个问题的一百万种方法之一=)
function shuffleArray(array = ["banana", "ovo", "salsicha", "goiaba", "chocolate"]) {
const newArray = [];
let number = Math.floor(Math.random() * array.length);
let count = 1;
newArray.push(array[number]);
while (count < array.length) {
const newNumber = Math.floor(Math.random() * array.length);
if (!newArray.includes(array[newNumber])) {
count++;
number = newNumber;
newArray.push(array[number]);
}
}
return newArray;
}
评论
O (n ^ 2)
使用排序方法和数学方法:
var arr = ["HORSE", "TIGER", "DOG", "CAT"];
function shuffleArray(arr){
return arr.sort( () => Math.floor(Math.random() * Math.floor(3)) - 1)
}
// every time it gives random sequence
shuffleArr(arr);
// ["DOG", "CAT", "TIGER", "HORSE"]
// ["HORSE", "TIGER", "CAT", "DOG"]
// ["TIGER", "HORSE", "CAT", "DOG"]
评论
基准
让我们先看看结果,然后我们将看看下面的每个实现 -shuffle
拼接速度慢
任何使用或循环的解决方案都会非常慢。当我们增加数组的大小时,这一点尤其明显。在一个幼稚的算法中,我们——splice
shift
- 在输入数组中获取一个位置, ,
rand
i
t
- 添加到输出
t[i]
splice
数组中的位置i
t
为了夸大慢效应,我们将在包含 100 万个元素的数组上演示这一点。下面的脚本差不多 30 秒 -
const shuffle = t =>
Array.from(sample(t, t.length))
function* sample(t, n)
{ let r = Array.from(t)
while (n > 0 && r.length)
{ const i = rand(r.length) // 1
yield r[i] // 2
r.splice(i, 1) // 3
n = n - 1
}
}
const rand = n =>
0 | Math.random() * n
function swap (t, i, j)
{ let q = t[i]
t[i] = t[j]
t[j] = q
return t
}
const size = 1e6
const bigarray = Array.from(Array(size), (_,i) => i)
console.time("shuffle via splice")
const result = shuffle(bigarray)
console.timeEnd("shuffle via splice")
document.body.textContent = JSON.stringify(result, null, 2)
body::before {
content: "1 million elements via splice";
font-weight: bold;
display: block;
}
流行音乐很快
诀窍不是使用,而是使用超高效。为此,代替典型的呼叫,您 -splice
pop
splice
- 选择要拼接的位置,
i
- 与最后一个元素交换,
t[i]
t[t.length - 1]
- 添加到结果
t.pop()
现在,我们可以在不到 100 毫秒的时间内处理 100 万个元素shuffle
-
const shuffle = t =>
Array.from(sample(t, t.length))
function* sample(t, n)
{ let r = Array.from(t)
while (n > 0 && r.length)
{ const i = rand(r.length) // 1
swap(r, i, r.length - 1) // 2
yield r.pop() // 3
n = n - 1
}
}
const rand = n =>
0 | Math.random() * n
function swap (t, i, j)
{ let q = t[i]
t[i] = t[j]
t[j] = q
return t
}
const size = 1e6
const bigarray = Array.from(Array(size), (_,i) => i)
console.time("shuffle via pop")
const result = shuffle(bigarray)
console.timeEnd("shuffle via pop")
document.body.textContent = JSON.stringify(result, null, 2)
body::before {
content: "1 million elements via pop";
font-weight: bold;
display: block;
}
更快
上述两个实现生成一个新的输出数组。输入数组未修改。这是我首选的工作方式,但是您可以通过就地洗牌来进一步提高速度。shuffle
在不到 10 毫秒的时间内低于 100 万个元素shuffle
-
function shuffle (t)
{ let last = t.length
let n
while (last > 0)
{ n = rand(last)
swap(t, n, --last)
}
}
const rand = n =>
0 | Math.random() * n
function swap (t, i, j)
{ let q = t[i]
t[i] = t[j]
t[j] = q
return t
}
const size = 1e6
const bigarray = Array.from(Array(size), (_,i) => i)
console.time("shuffle in place")
shuffle(bigarray)
console.timeEnd("shuffle in place")
document.body.textContent = JSON.stringify(bigarray, null, 2)
body::before {
content: "1 million elements in place";
font-weight: bold;
display: block;
}
我使用这两种方法:
此方法不修改原始数组
shuffle(array);
function shuffle(arr) {
var len = arr.length;
var d = len;
var array = [];
var k, i;
for (i = 0; i < d; i++) {
k = Math.floor(Math.random() * len);
array.push(arr[k]);
arr.splice(k, 1);
len = arr.length;
}
for (i = 0; i < d; i++) {
arr[i] = array[i];
}
return arr;
}
var arr = ["a", "b", "c", "d"];
arr = shuffle(arr);
console.log(arr);
此方法修改原始数组
array.shuffle();
Array.prototype.shuffle = function() {
var len = this.length;
var d = len;
var array = [];
var k, i;
for (i = 0; i < d; i++) {
k = Math.floor(Math.random() * len);
array.push(this[k]);
this.splice(k, 1);
len = this.length;
}
for (i = 0; i < d; i++) {
this[i] = array[i];
}
}
var arr = ["a", "b", "c", "d"];
arr.shuffle();
console.log(arr);
//doesn change array
Array.prototype.shuffle = function () {
let res = [];
let copy = [...this];
while (copy.length > 0) {
let index = Math.floor(Math.random() * copy.length);
res.push(copy[index]);
copy.splice(index, 1);
}
return res;
};
let a=[1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(a.shuffle());
const arr = [
{ index: 0, value: "0" },
{ index: 1, value: "1" },
{ index: 2, value: "2" },
{ index: 3, value: "3" },
];
let shuffle = (arr) => {
let set = new Set();
while (set.size != arr.length) {
let rand = Math.floor(Math.random() * arr.length);
set.add(arr[rand]);
}
console.log(set);
};
shuffle(arr);
这里使用简单的 while 循环
function ShuffleColor(originalArray) {
let shuffeledNumbers = [];
while (shuffeledNumbers.length <= originalArray.length) {
for (let _ of originalArray) {
const randomNumb = Math.floor(Math.random() * originalArray.length);
if (!shuffeledNumbers.includes(originalArray[randomNumb])) {
shuffeledNumbers.push(originalArray[randomNumb]);
}
}
if (shuffeledNumbers.length === originalArray.length)
break;
}
return shuffeledNumbers;
}
const colors = [
'#000000',
'#2B8EAD',
'#333333',
'#6F98A8',
'#BFBFBF',
'#2F454E'
]
ShuffleColor(colors)
我发现这很有用:
const shuffle = (array: any[]) => {
return array.slice().sort(() => Math.random() - 0.5);
}
console.log(shuffle([1,2,3,4,5,6,7,8,9,10]));
// Output: [4, 3, 8, 10, 1, 7, 9, 2, 6, 5]
评论
为了获得更大的灵活性,您可以添加另一个参数。在这种情况下,您可以从数组中获取一个随机数组并指定新数组的长度:
function shuffle(array, len = array.length) {
for (let i = array.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array.slice(0, len);
}
我找不到我喜欢的。这是我想出的一个解决方案。我没有使用太多无意义的变量,因为这就是我现在的编码方式。
Array.prototype.shuffle = function() {
for (let i in this) {
if (this.hasOwnProperty(i)) {
let index = Math.floor(Math.random() * i);
[
this[i],
this[index]
] = [
this[index],
this[i]
];
}
}
return this;
}
let arrayA = [
"item1", "item2", "item3", "item4", "item5"
];
Array.prototype.shuffle = function() {
for (let i in this) {
if (this.hasOwnProperty(i)) {
let index = Math.floor(Math.random() * i);
[
this[i],
this[index]
] = [
this[index],
this[i]
];
}
}
return this;
}
console.log(arrayA.shuffle());
我希望这对那些可能不太理解这一点的人有所帮助。
或者像上面所有的答案一样,但简而言之。
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 }
评论
随机化没有重复项的数组
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);
为了完整起见,除了 Fischer-Yates 的 Durstenfeld 变体之外,我还要指出 Sattolo 的算法,它只是一个微小的变化,导致每个元素都发生了变化。
function 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)
洗牌数组元素的可理解方式
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));
您可以使用随机播放。像魅力一样工作lodash
import _ from lodash;
let numeric_array = [2, 4, 6, 9, 10];
let string_array = ['Car', 'Bus', 'Truck', 'Motorcycle', 'Bicycle', 'Person']
let shuffled_num_array = _.shuffle(numeric_array);
let shuffled_string_array = _.shuffle(string_array);
console.log(shuffled_num_array, shuffled_string_array)
使用生成器函数的 ES6 紧凑代码*
其工作原理是从未洗牌数组的副本中随机删除项目,直到没有剩余项目。它使用新的 ES6 生成器功能。
只要 Math.random() 是公平的,这将是一个完全公平的洗牌。
let arr = [1,2,3,4,5,6,7]
function* shuffle(arr) {
arr = [...arr];
while(arr.length) yield arr.splice(Math.random()*arr.length|0, 1)[0]
}
console.log([...shuffle(arr)])
或者,使用 ES6 和拼接:
let arr = [1,2,3,4,5,6,7]
let shuffled = arr.reduce(([a,b])=>
(b.push(...a.splice(Math.random()*a.length|0, 1)), [a,b]),[[...arr],[]])[1]
console.log(shuffled)
或者,ES6 指数互换方法:
let arr = [1,2,3,4,5,6,7]
let shuffled = arr.reduce((a,c,i,r,j)=>
(j=Math.random()*(a.length-i)|0,[a[i],a[j]]=[a[j],a[i]],a),[...arr])
console.log(shuffled)
使用和forEach
Math.random()
var data = ['a','b','c','d','e']
data.forEach( (value,i) => {
var random = Math.floor(Math.random() * data.length)
var tmp = data[random]
data[random] = value
data[i] = tmp
})
console.log(data)
评论
我将在这里添加我最常使用的解决方案,因为我没有完全找到这种方法:
const shuffle = array =>
array
// Generate a random number for each elements
.map(value => [Math.random(), value])
// Sort using each element random number
.sort(([a], [b]) => a - b)
// Return back to an array of values
.map(entry => entry[1])
我喜欢它的是算法的简单性,为每个元素分配一个随机数,然后排序。
我觉得交换有点难以想象,当我必须从无到有时,我可以更有信心地想出这个,我认为它的作用非常清楚,而且我喜欢它没有到位,我的大部分 suffle 都在小数组上,所以这对我的用例非常有用。
我仍然希望我们内置了一个随机播放方法
评论
下一个:按字符串属性值对对象数组进行排序
评论
arr1.sort(() => (Math.random() > .5) ? 1 : -1);
a.sort(() => Math.random() - 0.5)