Ben böyle bir dizi var:
var arr1 = ["a", "b", "c", "d"];
Nasıl randomize edebilir / karıştırabilirim?
Ben böyle bir dizi var:
var arr1 = ["a", "b", "c", "d"];
Nasıl randomize edebilir / karıştırabilirim?
Yanıtlar:
Fiili tarafsız shuffle algoritması Fisher-Yates (Knuth) Shuffle'dır.
Bkz. Https://github.com/coolaj86/knuth-shuffle
Bir görebilirsiniz burada büyük görselleştirme (ve orijinal yayını bu bağlantılı )
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);
Kullanılan algoritma hakkında daha fazla bilgi .
i--
değil --i
. Ayrıca, deney if (i==0)...
eğer bu yana gereksiz olduğunu iken döngü girilebilir asla. İle çağrı daha hızlı yapılabilir . Her iki tempi veya tempj çıkarılabilir ve değerini doğrudan atanabilir myArray [I] ya da j uygun olarak. i == 0
Math.floor
...| 0
0 !== currentIndex
).
Fisher-Yates'in optimize edilmiş bir sürümü olan Durstenfeld shuffle'ın JavaScript uygulaması :
/* 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;
}
}
Her orijinal dizi öğesi için rastgele bir öğe seçer ve bunu bir kart destesinden rastgele seçmek gibi bir sonraki çizimden hariç tutar.
Bu akıllı dışlama, seçilen öğeyi geçerli öğeyle değiştirir, daha sonra bir sonraki rastgele öğeyi geri kalanından alır, optimum verimlilik için geriye doğru döngü yapar, rastgele seçmenin basitleştirilmesini sağlar (her zaman 0'dan başlayabilir) ve böylece son öğeyi atlar.
Algoritma çalışma zamanı O(n)
. Karıştırmanın yerinde yapıldığını unutmayın , böylece orijinal diziyi değiştirmek istemiyorsanız, önce bunun bir kopyasını alın .slice(0)
.
Yeni ES6 aynı anda iki değişken atamamıza izin veriyor. Bu, iki değişkenin değerlerini değiştirmek istediğimizde kullanışlıdır, çünkü bunu bir kod satırında yapabiliriz. İşte bu özelliği kullanarak aynı fonksiyonun daha kısa bir formu.
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, işlev bağımsız değişkenleri olarak kullanıldığında dizileri başvuru ile ilettiği için gerekli değildir . Bunun yığın alanından tasarruf etmek olduğunu düşünüyorum, ancak ilginç bir küçük özellik. Dizide karışık çalma işlemi gerçekleştirildiğinde orijinal dizi karıştırılır.
Math.random() should not be multiplied with the loop counter + 1, but with
array.lengt () '. Bkz. Belirli bir aralıkta JavaScript'te rasgele tam sayılar oluşturma? çok kapsamlı bir açıklama için.
Uyarı!
Bu algoritmanın kullanılması önerilmez , çünkü verimsiz ve güçlü bir şekilde önyargılıdır ; Yorumlara bakınız. Gelecekte referans olması için burada bırakılıyor, çünkü fikir o kadar nadir değil.
[1,2,3,4,5,6].sort(function() {
return .5 - Math.random();
});
Array'den bir prototip olarak kullanılabilir veya kullanılmalıdır:
ChristopheD'den:
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
diziler üzerinde yineleme yapmak için döngüler kullanmayın .
Harita ve sıralama ile kolayca yapabilirsiniz:
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)
Polimorfik dizileri karıştırabilirsiniz ve sıralama çoğu amaç için yeterince iyi olan Math.random kadar rasgele.
Öğeler, her yinelemeyi yeniden oluşturmayan tutarlı anahtarlara göre sıralandığından ve her karşılaştırma aynı dağıtımdan çekildiğinden, Math.random dağıtımındaki rasgele olmayan öğeler iptal edilir.
hız
Zaman karmaşıklığı O (N log N), hızlı sıralama ile aynıdır. Uzay karmaşıklığı O (N) 'dir. Bu bir Fischer Yates shuffle kadar etkili değil, ama bence, kod önemli ölçüde daha kısa ve daha işlevsel. Geniş bir diziniz varsa kesinlikle Fischer Yates kullanmalısınız. Birkaç yüz öğeden oluşan küçük bir diziniz varsa, bunu yapabilirsiniz.
Underscore.js kütüphanesini kullanın. Yöntem _.shuffle()
bu durum için güzel. İşte yöntem ile bir örnek:
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
işlev elde etmek için tüm kütüphaneyi dahil etmenin bir anlamı yok .
YENİ!
Daha kısa ve muhtemelen * daha hızlı Fisher-Yates shuffle algoritması
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
}
komut dosyası boyutu (işlev adı olarak fy ile): 90bayt
DEMO http://jsfiddle.net/vvpoma8w/
* muhtemelen krom hariç tüm tarayıcılarda daha hızlı.
Herhangi bir sorunuz varsa sorun.
DÜZENLE
evet daha hızlı
PERFORMANS: http://jsperf.com/fyshuffle
en çok oy alan fonksiyonları kullanarak.
DÜZENLEME Fazla hesaplama yapıldı (gerek yok --c + 1) ve kimse fark etmedi
daha kısa (4 bayt) ve daha hızlı (test edin!).
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
}
Başka bir yerde önbellekleme var rnd=Math.random
ve daha sonra kullanım rnd()
da büyük dizilerdeki performansı biraz artıracaktır.
http://jsfiddle.net/vvpoma8w/2/
Okunabilir sürüm (orijinal sürümü kullanın. Bu daha yavaştır, vars, "&"; kapanışları gibi işe yaramaz, kodun kendisi de daha kısadır ... belki bunu okuyun Javascript kodu nasıl 'küçültülür' , btw aşağıdaki kodu yukarıdaki gibi bir javascript küçültücüsünde sıkıştırın.)
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
ve shuffle prototype
ben, olsun fy
OS X 10.9.5 üzerinde Chrome 37 alt (% 81 daha yavaş ~ 20k op ~ 100k karşılaştırıldığında) ve Safari 7.1 de sürekli bunun% 8 daha yavaş ~ kalmış. YMMV, ama her zaman daha hızlı değil. jsperf.com/fyshuffle/3
Düzenleme: Bu cevap yanlış
Yorumlara ve https://stackoverflow.com/a/18650169/28234 adresine bakın . Burada referans olarak bırakılıyor çünkü fikir nadir değil.
Küçük diziler için çok basit bir yol şudur:
const someArray = [1, 2, 3, 4, 5];
someArray.sort(() => Math.random() - 0.5);
Muhtemelen çok verimli değil, ancak küçük diziler için bu iyi çalışıyor. İşte bir örnek, ne kadar rasgele (veya değil) olduğunu ve kullanıcı tabanınıza uyup uymadığını görebilirsiniz.
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>
Bu sayfadaki bazı çözümler güvenilir değildir (diziyi yalnızca kısmen rasgele). Diğer çözümler önemli ölçüde daha az verimlidir. İletestShuffleArrayFun
biz güvenilirlik ve performans için dizi karıştırma fonksiyonları test edebilirsiniz (aşağıya bakınız). Aşağıdaki çözümler şunlardır: güvenilir, verimli ve kısa (ES6 sözdizimi kullanılarak)
[Karşılaştırma testleri testShuffleArrayFun
Google Chrome'daki diğer çözümlerle karşılaştırıldı ]
Diziyi Karıştır
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 Pure, Yinelemeli
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
};
Bu sayfada görebileceğiniz gibi, geçmişte burada yanlış çözümler sunulmuştu. Ben yazdım ve herhangi bir saf (yan etkisi yok) dizi rastgele fonksiyonlarını test etmek için aşağıdaki işlevi kullandık.
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}`)
}
Sadece eğlence için diğer çözümler.
ES6 Saf, Özyinelemeli
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 kullanarak 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 kullanarak 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]]
? Belki bunun nasıl çalıştığını anlayabilirsiniz. Neden aşağı doğru yinelemeyi seçiyorsunuz?
@Laurens Holsts cevabına ekleniyor. Bu% 50 sıkıştırılmıştır.
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 dışında döngü bildirmek ve b =
bir döngü içinde atamak yerine bir döngüde yapmak verimli mi?
Bkz. Https://stackoverflow.com/a/18650169/28234 . Burada referans olarak bırakılıyor çünkü fikir nadir değil.
//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
pozitif veya negatif olabilen rastgele bir sayıdır, bu nedenle sıralama işlevi öğeleri rastgele yeniden sıralar.
ES2015 ile bunu kullanabilirsiniz:
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;
}
Kullanımı:
[1, 2, 3, 4, 5, 6, 7].shuffle();
n >>> 0
yerine kullanmalısınız ~~n
. Dizi indeksleri 2³¹-1'den yüksek olabilir.
Bu varyantı, bu sorunun kopyasında "yazar tarafından silindi" yanıtlarında takıldığımı buldum. Zaten çok sayıda oyu olan diğer cevapların aksine, bu:
shuffled
adı yerine shuffle
)İşte onu gösteren bir jsfiddle .
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(); });
- rastgele bir sıralama vermez ve kullanırsanız utanabilirsiniz: robweir.com/blog/2010/02/microsoft-random-browser-ballot.html
.sort(function(a,b){ return a[0] - b[0]; })
Sıralamanın değerleri sayısal olarak karşılaştırmasını istiyorsanız kullanmanız gerekir . Varsayılan .sort()
karşılaştırıcı bunun dikkate alacaktır, yani lexicographic olduğu 10
daha az olması 2
beri 1
az olduğunu 2
.
Math.random()
üreten aralıktaki sayılar için önemli değil . (yani sözlükbilimsel düzen, 0 (dahil) ile 1 (özel) arasındaki sayılarla uğraşırken sayısal sıralama ile aynıdır)
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);
Aşağıdakilerle kolayca yapabilirsiniz:
// array
var fruits = ["Banana", "Orange", "Apple", "Mango"];
// random
fruits.sort(function(a, b){return 0.5 - Math.random()});
// out
console.log(fruits);
Lütfen JavaScript Sıralama Dizileri'ne başvurun
Fisher-Yates javascript'te karışık. Bunu burada gönderiyorum çünkü iki yardımcı fonksiyonun (swap ve randInt) kullanımı, buradaki diğer cevaplara kıyasla algoritmayı netleştiriyor.
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);
}
}
Her şeyden önce, bir göz buraya javascript farklı sıralama yöntemlerinin büyük bir görsel karşılaştırma için.
İkincisi, yukarıdaki bağlantıya hızlı bir şekilde bakarsanız, random order
sıralamanın diğer yöntemlere kıyasla nispeten iyi performans gösterdiğini ve aşağıda gösterildiği gibi uygulanması son derece kolay ve hızlı olduğunu göreceksiniz :
function shuffle(array) {
var random = array.map(Math.random);
array.sort(function(a, b) {
return random[array.indexOf(a)] - random[array.indexOf(b)];
});
}
Düzenleme : @gregers tarafından işaret edildiği gibi, karşılaştırma işlevi endeksler yerine değerlerle çağrılır, bu yüzden kullanmanız gerekir indexOf
. Bu değişikliğin, kodu indexOf
O (n) zamanında çalıştığı gibi daha büyük diziler için daha az uygun hale getirdiğini unutmayın .
Array.prototype.sort
İki geçer değerleri olarak a
ve b
, dizine. Yani bu kod çalışmıyor.
Güncelleme : Burada küçük boyutlu dizilerle iyi sonuç verecek nispeten basit ( karmaşıklık perspektifinden değil ) ve kısa bir algoritma öneriyorum , ancak büyük dizilerle uğraşırken kesinlikle klasik Durstenfeld algoritmasından çok daha pahalıya mal olacak . Durstenfeld'i bu sorunun en önemli cevaplarından birinde bulabilirsiniz .
Orijinal cevap:
Eğer varsa istemeyen mutasyona sizin karıştır işlevini kaynak dizisi , daha sonra basit ile gerisini, yerel bir değişkene kopyalayabilirsiniz karıştırma mantığı .
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;
}
Karıştırma mantığı : rastgele bir dizin seçin, ardından karşılık gelen öğeyi sonuç dizisine ekleyin ve kaynak dizi kopyasından silin . Kaynak dizi boşalana kadar bu işlemi tekrarlayın .
Ve gerçekten kısa istiyorsanız, işte ne kadar uzağa gidebilirim:
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
"çarpıcı" dediklerini yapmanın korkunç verimsiz bir yoludur. Orijinal diziyi değiştirmek istemiyorsanız, sadece kopyalayın ve çok daha verimli Durstenfeld varyantını kullanarak bu kopyayı yerinde karıştırın.
splice
şöyle bir kopyasını oluşturmak için yöntem: source = array.slice();
.
İşte KOLAY olan,
function shuffle(array) {
array.sort(() => Math.random() - 0.5);
}
ayrıca örneğin, bunu kontrol edebilirsiniz burada
Fisher-Yates'in sıkı modunu kullanan bir başka uygulaması:
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;
}
Diğer tüm cevaplar, hızlı ancak şifreli seviye randomizasyonu için uygun olmayan Math.random () 'a dayanmaktadır.
Aşağıdaki kod, kriptografik randomizasyon seviyesi için iyi bilinen Fisher-Yates
algoritmayı kullanmaktadır .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));
Orijinal diziyi değiştirmeyen CoolAJ86 yanıtının basit bir değişikliği :
/**
* 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;
};
Zaten tavsiye edilen bir dizi uygulama olmasına rağmen, forEach döngüsünü kullanarak daha kısa ve daha kolay hale getirebileceğimizi hissediyorum, bu yüzden dizi uzunluğunu hesaplama konusunda endişelenmemize gerek yok ve ayrıca geçici bir değişken kullanmaktan güvenle kaçınabiliriz.
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)
Pastada bir parmak olsun. Burada Fisher Yates shuffle özyinelemeli bir uygulama sunuyoruz (sanırım). Düzgün bir rasgelelik verir.
Not: ~~
(çift tilde operatörü) aslında Math.floor()
pozitif gerçek sayılar gibi davranır . Sadece kısa bir yol.
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])));
Düzenleme: Yukarıdaki kod istihdam nedeniyle O (n ^ 2) .splice()
ama takas hilesi ile O (n) splice ve shuffle ortadan kaldırabilir.
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");
Sorun şu ki, JS büyük özyinelemelerle işbirliği yapamıyor. Bu durumda, dizi boyutu, tarayıcı motorunuza ve bazı bilinmeyen gerçeklere bağlı olarak 3000 ~ 7000 gibi sınırlıdır.
Diziyi rastgele seç
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
<
<=
en kısa arrayShuffle
fonksiyon
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;
}
Teorik bir bakış açısından, alçakgönüllülüğe göre, bunu yapmanın en zarif yolu, 0 ile n! -1 arasında tek bir rastgele sayı elde etmek ve{0, 1, …, n!-1}
(0, 1, 2, …, n-1)
. Herhangi bir önemli önyargı olmadan böyle bir sayı elde etmek için yeterince güvenilir bir (sözde) rasgele jeneratör kullanabileceğiniz sürece, diğer birkaç rasgele sayıya ihtiyaç duymadan istediğinizi elde etmek için yeterli bilgiye sahipsiniz.
IEEE754 çift kesinlikli kayar sayılarla hesaplama yaparken, rastgele üretecinizin yaklaşık 15 ondalık sayı vermesini bekleyebilirsiniz. Eğer sahip olduğundan 15! = 1,307,674,368,000 (13 hane ile) kullanarak, diziler 15 öğelere içeren ve diziler 14 elemente kadar içeren anlamlı önyargı olacak varsayalım ile aşağıdaki işlevleri kullanabilirsiniz. Bu karışık çalma işleminin birçok kez hesaplanmasını gerektiren sabit boyutlu bir sorun üzerinde çalışıyorsanız, yalnızca bir kez kullandığı için diğer kodlardan daha hızlı olabilecek aşağıdaki kodu denemek isteyebilirsiniz Math.random
(ancak birkaç kopyalama işlemi içerir).
Aşağıdaki işlev kullanılmayacak, ama yine de veriyorum; (0, 1, 2, …, n-1)
bu mesajda kullanılan bire bir eşleşmeye göre (permütasyonları numaralandırırken en doğal olanı) verilen bir permütasyonun indeksini döndürür ; 16 öğeye kadar çalışmak üzere tasarlanmıştır:
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);
}
Önceki fonksiyonun (kendi sorunuz için gerekli) karşılıklılığı aşağıdadır; 16 öğeye kadar çalışmak üzere tasarlanmıştır; o emri permütasyonunu döndüren n ait (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;
}
Şimdi, sadece istediğiniz şey:
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]; });
}
Biraz teorik önyargı ile 16 öğeye kadar çalışmalıdır (pratik açıdan fark edilmese de); 15 element için tamamen kullanılabilir olarak görülebilir; 14'ten az eleman içeren dizilerle, kesinlikle yanlılık olmayacağını düşünebilirsiniz.