私はこのような配列を持っています:
var arr1 = ["a", "b", "c", "d"];
ランダム化/シャッフルするにはどうすればよいですか?
私はこのような配列を持っています:
var arr1 = ["a", "b", "c", "d"];
ランダム化/シャッフルするにはどうすればよいですか?
回答:
事実上の不偏シャッフルアルゴリズムは、Fisher-Yates(別名Knuth)シャッフルです。
https://github.com/coolaj86/knuth-shuffleを参照してください
あなたはここで素晴らしい視覚化を見ることができます(そしてこれにリンクされた元の投稿)
function shuffle(array) {
var currentIndex = array.length, temporaryValue, randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
// Used like so
var arr = [2, 11, 37, 42];
shuffle(arr);
console.log(arr);
使用されているアルゴリズムに関する詳細情報。
i--
ません--i
。また、テストがif (i==0)...
あれば以来、余分である間のループが入力されることはありません。への呼び出しは、を使用してより速く行うことができます。いずれかのテンポ又はtempjを除去することができ、値を直接に割り当てることがmyarrayの[I]またはJ適宜。i == 0
Math.floor
...| 0
0 !== currentIndex
)。
次に、Fisher-Yatesの最適化されたバージョンであるDurstenfeld shuffleの 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)
です。注あなたが元の配列を変更したくない場合は、最初にそのコピーを作成してシャッフルをその場で行われていること.slice(0)
。
新しいES6では、2つの変数を一度に割り当てることができます。これは、1行のコードで実行できるため、2つの変数の値を交換する場合に特に便利です。この機能を使用した同じ関数の短縮形を次に示します。
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]];
}
}
return array
JavaScriptは、関数の引数として使用される場合、参照によって配列を渡すため、必須ではありません。これはスタックスペースを節約するためだと思いますが、これは興味深い小さな機能です。アレイでシャッフルを実行すると、元のアレイがシャッフルされます。
Math.random() should not be multiplied with the loop counter + 1, but with
array.lengt() `。JavaScriptで特定の範囲のランダムな整数を生成するをご覧ください。非常に包括的な説明のために。
警告!
このアルゴリズムの使用は非効率的であり、偏りが強いため、お勧めできません。コメントを参照してください。アイデアはそれほど珍しいものではないので、将来の参照用にここに残しています。
[1,2,3,4,5,6].sort(function() {
return .5 - Math.random();
});
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;
}
for...in
ループを使用して配列を反復しないでください。
あなたはマップとソートでそれを簡単に行うことができます:
let unshuffled = ['hello', 'a', 't', 'q', 1, 2, 3, {cats: true}]
let shuffled = unshuffled
.map((a) => ({sort: Math.random(), value: a}))
.sort((a, b) => a.sort - b.sort)
.map((a) => a.value)
ポリモーフィック配列をシャッフルでき、ソートはMath.randomと同じくらいランダムで、ほとんどの目的に十分です。
要素は、反復ごとに再生成されない一貫したキーに対してソートされ、各比較は同じ分布からプルするため、Math.randomの分布の非ランダム性はキャンセルされます。
速度
時間の複雑さはO(N log N)で、クイックソートと同じです。スペースの複雑さはO(N)です。これはフィッシャーイェーツのシャッフルほど効率的ではありませんが、私の意見では、コードは大幅に短く、機能的です。配列が大きい場合は、必ずフィッシャーイェーツを使用してください。数百項目の小さな配列がある場合は、これを行うことができます。
underscore.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();
shuffle
関数を取得するためだけにライブラリ全体を含める意味はありません 。
新着!
より短く、おそらく*より速いフィッシャーイェイツのシャッフルアルゴリズム
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/
*おそらくクロームを除くすべてのブラウザでより高速です。
ご不明な点がございましたらお尋ねください。
編集する
はい、それはより速いです
パフォーマンス: 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 minifiersで次のコードを圧縮します。)
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
OS X 10.9.5(〜100kと比較して81%遅い〜20k ops)のChrome 37とSafari 7.1は常に最大〜8%遅くなります。YMMVですが、常に高速であるとは限りません。 jsperf.com/fyshuffle/3
編集:この答えは正しくありません
コメントと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>
このページのいくつかのソリューションは信頼できません(アレイを部分的にランダム化するだけです)。その他のソリューションは、効率が大幅に低下します。ではtestShuffleArrayFun
、我々は信頼性とパフォーマンスのために、アレイシャッフル機能をテストすることができます(下記参照)。次のソリューションは、信頼性が高く、効率的で短い(ES6構文を使用)
[ testShuffleArrayFun
Google Chromeで他のソリューションに対して比較テストが行われました]
配列をシャッフルする
function getShuffledArr (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
};
このページでわかるように、過去にここで提供された誤ったソリューションがありました。次の関数を記述して、純粋な(副作用のない)配列ランダム化関数をテストしました。
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))];
};
array.mapを使用したES6 Pure
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]
})
}
array.reduceを使用したES6 Pure
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]]
?多分あなたはそれがどのように機能するかを概説できます。なぜ下向き反復を選択するのですか?
@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 b =
ループ外でbを宣言してループ内で割り当てる代わりに、ループ内で行うのが効率的b =
ですか?
https://stackoverflow.com/a/18650169/28234を参照してください。アイデアは珍しくないので、参照用にここに残しています。
//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
は正または負の乱数であるため、並べ替え関数は要素をランダムに並べ替えます。
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
。配列のインデックスは2³¹-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(); });
-ランダムな並べ替えは行われません。使用すると、困惑する
.sort(function(a,b){ return a[0] - b[0]; })
並べ替えで数値を比較する場合に使用する必要があります。デフォルトの.sort()
コンパレータは辞書式です。つまり、はより10
小さいと見なされます。2
1
2
Math.random()
生成される範囲の数値では問題になりません。(1(排他的)に0(包括的)から数字を扱うときであること、辞書式順序は数値順と同じである)
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;
};
arr1.sort(() => Math.random() - 0.5);
あなたはそれを簡単に行うことができます:
// array
var fruits = ["Banana", "Orange", "Apple", "Mango"];
// random
fruits.sort(function(a, b){return 0.5 - Math.random()});
// out
console.log(fruits);
JavaScript Sorting Arraysで参照してください
フィッシャーイェーツがJavaScriptでシャッフルします。2つのユーティリティ関数(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);
}
}
まず、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で指摘されているように、compare関数はインデックスではなく値で呼び出されるため、を使用する必要がありますindexOf
。この変更により、indexOf
O(n)時間で実行されるため、コードがより大きな配列に適さなくなります。
Array.prototype.sort
インデックスではなく、およびとして2つの値を渡します。したがって、このコードは機能しません。a
b
更新:ここでは、比較的単純な(複雑さの観点からではなく)短いアルゴリズムを提案します。このアルゴリズムは、小さなサイズの配列で問題なく機能しますが、巨大な配列を処理する場合、従来の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;
}
splice
が彼らが「ストライクアウト」と呼んだことを実行するための恐ろしく非効率的な方法です。元の配列を変更したくない場合は、それをコピーしてから、より効率的なDurstenfeldバリアントを使用して、そのコピーを所定の場所に入れ替えます。
splice
メソッドを使用して、次のようなコピーを作成することもできますsource = array.slice();
。
厳格モードを使用した、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;
}
他のすべての答えは、高速ですが暗号レベルのランダム化には適さない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));
元の配列を変更しない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;
};
すでに推奨されている実装は多数ありますが、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)
パイに指を入れるだけ。ここでは、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)を採用しているため.splice()
、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程度に制限されます。
配列をランダム化
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
あなたが使って<
いなかったのでfor n があってはいけません<=
最短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;
}
理論的な観点から、私の考えでは、最もエレガントな方法は、0とn!-1の間の単一の乱数を取得し、からすべての順列への1対1のマッピングを計算することです。{0, 1, …, n!-1}
(0, 1, 2, …, n-1)
。(疑似)乱数発生器を使用して、大きなバイアスなしにそのような数値を取得するのに十分な信頼性がある限り、他のいくつかの乱数を必要とせずに目的を達成するための十分な情報があります。
IEEE754倍精度浮動小数点数で計算する場合、ランダムジェネレーターが約15の10進数を提供することを期待できます。あなたが持っているので= 1,307,674,368,000 15!(13桁)を、あなたは、15個の要素までを含む配列で、以下の機能を使用し、14個の要素までを含むアレイと有意な偏りが生じないと仮定することができます。このシャッフル操作を何度も計算する必要がある固定サイズの問題に取り組む場合、1回しか使用しないため、他のコードよりも高速である可能性がある次のコードを試してくださいMath.random
(ただし、複数のコピー操作が含まれます)。
次の関数は使用しませんが、とにかく与えます。(0, 1, 2, …, n-1)
このメッセージで使用されている1対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個の要素を処理することを目的としています。次の順序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未満の配列の場合は、バイアスがないと考えることができます。