如何随机化(洗牌)JavaScript数组?

我有一个这样的数组:

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

我如何随机化/洗牌?

1340059 次浏览

事实上的无偏Shuffle算法是费舍尔-耶茨(又名高德纳)洗牌

您可以看到很棒的可视化(和原始帖子与此相关

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 sovar arr = [2, 11, 37, 42];shuffle(arr);console.log(arr);

更多信息关于算法使用。

可以(但不应该)将其用作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洗牌的JavaScript实现,这是费舍尔-耶茨的优化版本:

/* 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开始),从而跳过最终元素。

算法运行时是O(n)说明,洗牌是就地完成的,所以如果你不想修改原始数组,首先用#1复制它。


编辑:更新到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]];}}

使用underscore.js库。方法_.shuffle()很适合这种情况。下面是一个方法示例:

var _ = require("underscore");
var arr = [1,2,3,4,5,6];// Testing _.shufflevar 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();

添加到@Laurens Holsts答案。这是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 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教程直接解释了差异。

另一个实现的费舍尔-耶茨,使用严格模式:

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

费舍尔-耶茨的这种变体稍微更有效,因为它避免了与自身交换元素:

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,iwhile(len){i=Math.random()*len-- |0;temp=this[len],this[len]=this[i],this[i]=temp;}return this;}

新!

更短且可能更快的费舍尔-耶茨洗牌算法

  1. 它使用while---
  2. 按位到楼层(最多10位十进制数字(32位))
  3. 删除不必要的闭包和其他东西

function fy(a,b,c,d){//array,placeholder,placeholder,placeholderc=a.length;while(c)b=Math.random()*(--c+1)|0,d=a[c],a[c]=a[b],a[b]=d}

脚本大小(以fy为函数名):90字节

DEMOhttp://jsfiddle.net/vvpoma8w/

*在除chrome之外的所有浏览器上可能更快。

如果你有任何问题,只是问。

编辑

是的,它更快

性能:http://jsperf.com/fyshuffle

使用最高投票功能。

编辑有一个计算过量(不需要--c+1)却没有人注意到

更短(4字节)和更快(测试它!)。

function fy(a,b,c,d){//array,placeholder,placeholder,placeholderc=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}}

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

demo

首先,看看这里,看看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所指出的,使用值而不是索引调用比较函数,这就是为什么您需要使用indexOf。请注意,此更改使代码不太适合较大的数组,因为indexOf在O(n)时间内运行。

随机化数组

 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 tempArrtempArr.push(arr.splice(Math.floor(Math.random()*arr.length),1)[0]);}
// Push the remaining item onto tempArrtempArr.push(arr[0]);arr=tempArr;

我在这个问题的副本的“作者删除”答案中发现了这个变体。与其他一些已经有很多赞成票的答案不同,这是:

  1. 其实是随机的
  2. 不到位(因此名称为shuffled而不是shuffle
  3. 这里还没有出现多个变体

这是一个jsfiddle显示它在使用中

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

费希尔-耶茨在javascript中洗牌。我在这里发布这个是因为使用两个实用函数(交换和randInt)与这里的其他答案相比,澄清了算法。

function swap(arr, i, j) {// swaps two elements of an array in placevar 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 arrayvar arrRand = []; //this will give shuffled arrayvar arrTempInd = []; // to store shuffled indexesvar max = inpArr.length;var min = 0;var tempInd;var i = 0;
do{//generate random index between rangetempInd = Math.floor(Math.random() * (max - min));//check if index is already available in array to avoid repetitionif(arrTempInd.indexOf(tempInd)<0){//push character at random indexarrRand[i] = inpArr[tempInd];//push random indexesarrTempInd.push(tempInd);i++;}}// check if random array length is equal to input array lengthwhile(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();

考虑将其应用于代替或新的不可变数组,遵循其他解决方案,这里是一个建议的实现:

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

罗纳德·费舍尔和弗兰克·耶茨

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

喷气优化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;}

$=(m)=>console.log(m);
//----add this method to Array classArray.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());

最短的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 abovevar 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 wantedfor (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 herefor (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));}

从理论的角度来看,最优雅的方法,在我的愚见中,是在n!-1之间获得一个单一随机数,并计算从{0, 1, …, n!-1}(0, 1, 2, …, n-1)的所有排列的一对一映射。只要你能使用一个足够可靠的(伪)随机生成器来获得这样一个数字而没有任何明显的偏差,你就有足够的信息来实现你想要的,而不需要其他几个随机数。

当使用IEEE754双精度浮点数计算时,你的随机生成器可以提供大约15个小数。由于你有15!=1,307,674,368,000(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个元素的数组,您可以放心地认为绝对没有偏差。

我在考虑在控制台中粘贴oneliner。.sort的所有技巧都给出了错误的结果,这是我的实现:

 ['Bob', 'Amy', 'Joy'].map((person) => `${Math.random().toFixed(10)}${person}`).sort().map((person) => person.substr(12));

但是不要在生产代码中使用它,它不是最佳的,只适用于字符串。

编辑:这个答案不正确

请参阅评论和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 parametersconst newArray = array.map(() => null);
// Create a new array where each index contain the index valueconst arrayReference = array.map((item, index) => index);
// Iterate on the array given in the parametersarray.forEach(randomize);        
return newArray;
function randomize(item) {const randomIndex = getRandomIndex();
// Replace the value in the new arraynewArray[arrayReference[randomIndex]] = item;            
// Remove in the array reference the index usedarrayReference.splice(randomIndex,1);}
// Return a number between 0 and current array reference lengthfunction 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])));

编辑:由于使用了.splice(),上面的代码是O(n^2),但我们可以通过交换技巧消除O(n)中的拼接和洗牌。

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纯迭代

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 = 1000000for (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纯使用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纯使用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])}

所有其他答案都基于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)

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

您可以对多态数组进行洗牌,排序与Math.random一样随机,这对于大多数目的来说已经足够了。

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

速度

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

一个不改变源数组的Shuffle函数

更新:这里我建议一个相对的简单(不是从复杂性的角度)和算法,它们可以很好地处理小尺寸的数组,但当你处理巨大的数组时,它肯定会比经典的Durstenfeld算法花费更多。你可以在这个问题的顶部回复中找到Durstenfeld

原答复:

如果你用不希望函数来改变源阵列,你可以把它复制到一个局部变量,然后用一个简单的洗牌逻辑来完成剩下的工作。

function shuffle(array) {var result = [], source = array.concat([]);
while (source.length) {let index = Math.floor(Math.random() * source.length);result.push(source[index]);source.splice(index, 1);}
return result;}

洗牌逻辑:选择一个随机索引,然后将相应的元素添加到结果数组并从源数组拷贝中删除它。重复此操作,直到源数组获得

如果你真的想要它短,这是我能走多远:

function shuffle(array) {var result = [], source = array.concat([]);
while (source.length) {let index = Math.floor(Math.random() * source.length);result.push(source.splice(index, 1)[0]);}
return result;}

我自己编写了一个Shuffle函数。这里的区别是它永远不会重复一个值(为此检查代码):-

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

(用于教育目的)

虽然已经建议了许多实现,但我觉得我们可以使用for每个循环使其更短,更容易,所以我们不需要担心计算数组长度,也可以安全地避免使用临时变量。

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 valuesconsole.log('Shuffled Array: ', myArr)

通过使用混洗阵列模块,您可以洗牌您的数组。这是它的简单代码。

var shuffle = require('shuffle-array'),//collection = [1,2,3,4,5];collection = ["a","b","c","d","e"];shuffle(collection);
console.log(collection);

希望这有帮助。

你可以很容易地做到这一点:

// arrayvar fruits = ["Banana", "Orange", "Apple", "Mango"];// randomfruits.sort(function(a, b){return 0.5 - Math.random()});// outconsole.log(fruits);

请参考JavaScript排序数组

d3.js提供了费希尔-耶茨洗牌内置版本:

console.log(d3.shuffle(["a", "b", "c", "d"]));
<script src="http://d3js.org/d3.v5.min.js"></script>

d3.shuffle(数组[, lo[, hi]])<>

使用费希尔-耶茨洗牌随机化指定数组的顺序。

使用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.shuffle

//one line solutionshuffle = (array) => array.sort(() => Math.random() - 0.5);

//Demolet 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);

警告!
不推荐将此答案用于随机化大型数组、密码学或任何其他需要真随机性的应用程序,因为它的偏见和低效率。元素位置只是半随机化的,它们将倾向于保持更接近它们的原始位置。见https://stackoverflow.com/a/18650169/28234


您可以使用Math.random任意决定是否返回1 : -1

[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%}

社区说arr.sort((a, b) => 0.5 - Math.random())不是100%随机的!
是的!我测试并推荐不要使用这种方法!

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)

正如你所看到的,这不是那么随机!soo…不要使用这种方法!


如果你测试多次,你会看到玩家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);}

例如,您可以检查它这里

使用费希尔-耶茨 Shuffle算法和ES6:

// Original arraylet array = ['a', 'b', 'c', 'd'];
// Create a copy of the original array to be randomizedlet shuffle = [...array];
// Defining function returning random value from i to Nconst getRandomValue = (i, N) => Math.floor(Math.random() * (N - i) + i);
// Shuffle a pair of two elements at random position jshuffle.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;

}

使用排序方法和Math方法:

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 sequenceshuffleArr(arr);// ["DOG", "CAT", "TIGER", "HORSE"]// ["HORSE", "TIGER", "CAT", "DOG"]// ["TIGER", "HORSE", "CAT", "DOG"]

基准

让我们先看看结果,然后我们将看看下面shuffle的每个实现-

  • 拼接

  • pop

  • inplace


拼接很慢

任何在循环中使用spliceshift的解决方案都会非常慢。当我们增加数组的大小时,这一点尤其明显。在朴素算法中,我们-

  1. 在输入数组中获得rand位置,it
  2. t[i]添加到输出
  3. splice位置i从数组t

为了夸大缓慢的效果,我们将在一百万个元素的数组上演示这一点。以下脚本快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) // 1yield r[i]               // 2r.splice(i, 1)           // 3n = n - 1}}
const rand = n =>Math.floor(Math.random() * n)
function swap (t, i, j){ let q = t[i]t[i] = t[j]t[j] = qreturn t}
const size = 1e6const 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;}


pop很快

诀窍是不要splice,而是使用超级高效的pop。要做到这一点,代替典型的splice调用,您-

  1. 选择要拼接的位置,i
  2. 用最后一个元素t[t.length - 1]交换t[i]
  3. t.pop()添加到结果

现在我们可以在小于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) // 1swap(r, i, r.length - 1) // 2yield r.pop()            // 3n = n - 1}}
const rand = n =>Math.floor(Math.random() * n)
function swap (t, i, j){ let q = t[i]t[i] = t[j]t[j] = qreturn t}
const size = 1e6const 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万个元素-

function shuffle (t){ let last = t.lengthlet nwhile (last > 0){ n = rand(last)swap(t, n, --last)}}
const rand = n =>Math.floor(Math.random() * n)
function swap (t, i, j){ let q = t[i]t[i] = t[j]t[j] = qreturn t}
const size = 1e6const 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 arrayArray.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);

这里有一个简单的同时循环

 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 }

不带dublicates的随机数组

    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}

不同之处在于如何计算随机索引jMath.random() * iMath.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)