Saya memiliki array seperti ini:
var arr1 = ["a", "b", "c", "d"];
Bagaimana saya bisa mengacak / mengacaknya?
Saya memiliki array seperti ini:
var arr1 = ["a", "b", "c", "d"];
Bagaimana saya bisa mengacak / mengacaknya?
Jawaban:
Algoritma acak shuffle tidak bias de-facto adalah Shuffle Fisher-Yates (alias Knuth).
Lihat https://github.com/coolaj86/knuth-shuffle
Anda dapat melihat visualisasi yang hebat di sini (dan pos asli yang ditautkan dengan ini )
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);
Beberapa info lebih lanjut tentang algoritma yang digunakan.
i--
tidak --i
. Juga, tes if (i==0)...
ini berlebihan karena jika i == 0
itu sementara lingkaran akan pernah masuk. Panggilan ke Math.floor
dapat dilakukan dengan lebih cepat menggunakan ...| 0
. Baik tempi atau tempj dapat dihapus dan nilainya langsung ditugaskan ke myArray [i] atau j yang sesuai.
0 !== currentIndex
).
Berikut ini adalah implementasi JavaScript dari Durstenfeld shuffle , versi Fisher-Yates yang dioptimalkan:
/* 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;
}
}
Ini mengambil elemen acak untuk setiap elemen array asli, dan mengecualikannya dari undian berikutnya, seperti memilih secara acak dari setumpuk kartu.
Pengecualian pintar ini menukar elemen yang dipilih dengan elemen saat ini, kemudian mengambil elemen acak berikutnya dari sisanya, mengulang ke belakang untuk efisiensi optimal, memastikan pengambilan acak disederhanakan (selalu dapat mulai dari 0), dan dengan demikian melewatkan elemen terakhir.
Algoritma runtime adalah O(n)
. Perhatikan bahwa pengocokan dilakukan di tempat jadi jika Anda tidak ingin memodifikasi array asli, pertama-tama buat salinannya .slice(0)
.
ES6 baru memungkinkan kita untuk menetapkan dua variabel sekaligus. Ini sangat berguna ketika kita ingin menukar nilai dari dua variabel, karena kita dapat melakukannya dalam satu baris kode. Ini adalah bentuk yang lebih pendek dari fungsi yang sama, menggunakan fitur ini.
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
karena JavaScript melewati array dengan referensi ketika digunakan sebagai argumen fungsi. Saya berasumsi ini untuk menghemat ruang stack, tapi ini fitur kecil yang menarik. Melakukan shuffle pada array akan mengocok array asli.
Math.random() should not be multiplied with the loop counter + 1, but with
array.lengt () `. Lihat Menghasilkan bilangan bulat acak dalam JavaScript dalam rentang tertentu? untuk penjelasan yang sangat komprehensif.
Peringatan!
Penggunaan algoritma ini tidak disarankan , karena tidak efisien dan sangat bias ; lihat komentar. Itu ditinggalkan di sini untuk referensi di masa depan, karena idenya tidak terlalu langka.
[1,2,3,4,5,6].sort(function() {
return .5 - Math.random();
});
Seseorang dapat (atau seharusnya) menggunakannya sebagai protoype dari Array:
Dari 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
loop untuk beralih di atas array.
Anda dapat melakukannya dengan mudah dengan memetakan dan mengurutkan:
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)
Anda dapat mengocok array polimorfik, dan pengurutannya acak seperti Math.random, yang cukup baik untuk sebagian besar tujuan.
Karena elemen diurutkan berdasarkan kunci konsisten yang tidak dibuat ulang setiap iterasi, dan setiap perbandingan menarik dari distribusi yang sama, setiap non-keacakan dalam distribusi Math.random dibatalkan.
Kecepatan
Kompleksitas waktu adalah O (N log N), sama dengan quick sort. Kompleksitas ruang adalah O (N). Ini tidak seefisien shuffle Fischer Yates tetapi, menurut saya, kode ini secara signifikan lebih pendek dan lebih fungsional. Jika Anda memiliki array besar, Anda tentu harus menggunakan Fischer Yates. Jika Anda memiliki array kecil dengan beberapa ratus item, Anda dapat melakukan ini.
Gunakan perpustakaan underscore.js. Metode _.shuffle()
ini bagus untuk kasus ini. Berikut ini adalah contoh dengan metode ini:
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
fungsi.
BARU!
Algoritma shuffle Fisher-Yates lebih pendek & mungkin lebih cepat
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
}
ukuran skrip (dengan fy sebagai nama fungsi): 90bytes
DEMO http://jsfiddle.net/vvpoma8w/
* lebih cepat mungkin di semua browser kecuali chrome.
Jika Anda memiliki pertanyaan, tanyakan saja.
EDIT
ya itu lebih cepat
KINERJA: http://jsperf.com/fyshuffle
menggunakan fungsi terpilih teratas.
EDIT Ada perhitungan berlebihan (tidak perlu --c + 1) dan tidak ada yang memperhatikan
lebih pendek (4bytes) & lebih cepat (coba!)
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
}
Caching di tempat lain var rnd=Math.random
lalu gunakanrnd()
juga akan sedikit meningkatkan kinerja pada array besar.
http://jsfiddle.net/vvpoma8w/2/
Versi yang dapat dibaca (gunakan versi asli. Ini lebih lambat, vars tidak berguna, seperti penutupan & ";", kode itu sendiri juga lebih pendek ... mungkin baca ini Cara 'memperkecil' kode Javascript , tetapi Anda tidak dapat kompres kode berikut dalam minifiers javascript seperti yang di atas.)
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
dan shuffle prototype
, saya mendapatkan fy
secara konsisten di bagian bawah di Chrome 37 pada OS X 10.9.5 (81% lebih lambat ~ 20k ops dibandingkan dengan ~ 100k) dan Safari 7.1 itu hingga ~ 8% lebih lambat. YMMV, tapi itu tidak selalu lebih cepat. jsperf.com/fyshuffle/3
Sunting: Jawaban ini salah
Lihat komentar dan https://stackoverflow.com/a/18650169/28234 . Itu ditinggalkan di sini untuk referensi karena idenya tidak jarang.
Cara yang sangat sederhana untuk array kecil adalah ini:
const someArray = [1, 2, 3, 4, 5];
someArray.sort(() => Math.random() - 0.5);
Ini mungkin tidak terlalu efisien, tetapi untuk array kecil ini berfungsi dengan baik. Berikut ini contoh sehingga Anda dapat melihat seberapa acak (atau tidak) itu, dan apakah itu cocok dengan usecase atau tidak.
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>
Beberapa solusi pada halaman ini tidak dapat diandalkan (mereka hanya mengacak sebagian array). Solusi lain secara signifikan kurang efisien. DengantestShuffleArrayFun
(lihat di bawah) kami dapat menguji fungsi pengocokan array untuk keandalan dan kinerja. Solusi berikut adalah: dapat diandalkan, efisien dan pendek (menggunakan sintaks ES6)
[Pengujian perbandingan dilakukan dengan menggunakan testShuffleArrayFun
solusi lain, di Google Chrome]
Shuffle Array In place
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 Murni, Berulang
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
};
Seperti yang Anda lihat di halaman ini, ada solusi yang salah yang ditawarkan di sini di masa lalu. Saya menulis dan telah menggunakan fungsi berikut untuk menguji fungsi pengacakan array murni (tanpa efek samping).
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}`)
}
Solusi lain hanya untuk bersenang-senang.
ES6 Murni, Rekursif
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 menggunakan 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 menggunakan 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]]
? Mungkin Anda bisa menguraikan cara kerjanya. Mengapa Anda memilih untuk beralih ke bawah?
Menambahkan ke jawaban @Laurens Holsts. Ini dikompresi 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 =
dalam satu lingkaran daripada mendeklarasikan b di luar lingkaran dan menugaskannya b =
dalam satu lingkaran?
Lihat https://stackoverflow.com/a/18650169/28234 . Itu ditinggalkan di sini untuk referensi karena idenya tidak jarang.
//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
adalah angka acak yang mungkin positif atau negatif, sehingga fungsi pengurutan akan menata ulang elemen secara acak.
Dengan ES2015 Anda dapat menggunakan ini:
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;
}
Pemakaian:
[1, 2, 3, 4, 5, 6, 7].shuffle();
n >>> 0
bukan ~~n
. Indeks array bisa lebih tinggi dari 2³¹-1.
Saya menemukan varian ini nongkrong di jawaban "dihapus oleh penulis" pada duplikat dari pertanyaan ini. Tidak seperti beberapa jawaban lain yang sudah memiliki banyak upvotes, ini adalah:
shuffled
namanya, bukan shuffle
)Ini jsfiddle yang menunjukkan sedang digunakan .
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(); });
- tidak memberikan penyortiran acak, dan jika Anda menggunakannya, Anda bisa merasa malu: robweir.com/blog/2010/02/microsoft-random-browser-ballot.html
.sort(function(a,b){ return a[0] - b[0]; })
jika Anda ingin menyortir untuk membandingkan nilai secara numerik. .sort()
Komparator default adalah leksikografis, artinya akan dianggap 10
kurang dari 2
karena 1
kurang dari 2
.
Math.random()
menghasilkan. (yaitu, urutan leksikografis sama dengan urutan numerik ketika berhadapan dengan angka dari 0 (inklusif) hingga 1 (eksklusif))
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);
Anda dapat melakukannya dengan mudah dengan:
// array
var fruits = ["Banana", "Orange", "Apple", "Mango"];
// random
fruits.sort(function(a, b){return 0.5 - Math.random()});
// out
console.log(fruits);
Silakan referensi di Array Penyortiran JavaScript
Fisher-Yates mengacak dalam javascript. Saya memposting ini di sini karena penggunaan dua fungsi utilitas (swap dan randInt) memperjelas algoritme dibandingkan dengan jawaban lain di sini.
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);
}
}
Pertama-tama, lihat di sini untuk perbandingan visual yang bagus dari berbagai metode penyortiran dalam javascript.
Kedua, jika Anda melihat sekilas tautan di atas, Anda akan menemukan bahwa random order
pengurutan tersebut tampaknya berkinerja relatif baik dibandingkan dengan metode lain, sementara sangat mudah dan cepat untuk diterapkan seperti yang ditunjukkan di bawah ini:
function shuffle(array) {
var random = array.map(Math.random);
array.sort(function(a, b) {
return random[array.indexOf(a)] - random[array.indexOf(b)];
});
}
Sunting : seperti yang ditunjukkan oleh @gregers, fungsi membandingkan disebut dengan nilai daripada indeks, itulah sebabnya Anda perlu menggunakan indexOf
. Perhatikan bahwa perubahan ini membuat kode kurang cocok untuk array yang lebih besar karena indexOf
berjalan dalam waktu O (n).
Array.prototype.sort
melewati dua nilai sebagai a
dan b
, bukan indeks. Jadi kode ini tidak berfungsi.
Pembaruan : Di sini saya menyarankan algoritma yang relatif sederhana (bukan dari perspektif kompleksitas ) dan pendek yang akan baik-baik saja dengan array berukuran kecil, tapi itu pasti akan jauh lebih mahal daripada algoritma Durstenfeld klasik ketika Anda berurusan dengan array besar. Anda dapat menemukan Durstenfeld di salah satu balasan teratas untuk pertanyaan ini.
Jawaban asli:
Jika Anda tidak ingin fungsi shuffle Anda untuk mengubah array sumber , Anda dapat menyalinnya ke variabel lokal, lalu lakukan sisanya dengan logika pengocokan sederhana .
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;
}
Logika pengocokan : ambil indeks acak, lalu tambahkan elemen yang sesuai ke array hasil dan hapus dari salinan array sumber . Ulangi tindakan ini sampai array sumber menjadi kosong .
Dan jika Anda benar-benar menginginkannya pendek, berikut ini sejauh yang bisa saya dapatkan:
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
menjadi cara yang sangat tidak efisien untuk melakukan apa yang mereka sebut "mencolokkan". Jika Anda tidak ingin mengubah array asli, salin saja, lalu kocok salinan itu menggunakan varian Durstenfeld yang jauh lebih efisien.
splice
metode untuk membuat salinan seperti: source = array.slice();
.
Inilah yang TERMUDAH ,
function shuffle(array) {
return array.sort(() => Math.random() - 0.5);
}
untuk contoh lebih lanjut, Anda dapat memeriksanya di sini
Belum lagi implementasi Fisher-Yates, menggunakan mode ketat:
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;
}
Semua jawaban lain didasarkan pada Math.random () yang cepat tetapi tidak cocok untuk pengacakan tingkat cryptgraphic.
Kode di bawah ini menggunakan terkenal Fisher-Yates
algoritma sambil memanfaatkan Web Cryptography API
untuk tingkat kriptografi pengacakan .
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));
Modifikasi sederhana dari jawaban CoolAJ86 yang tidak mengubah array asli:
/**
* 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;
};
Meskipun ada beberapa implementasi yang sudah disarankan tetapi saya merasa kita bisa membuatnya lebih pendek dan lebih mudah menggunakan forEach loop, jadi kita tidak perlu khawatir tentang menghitung panjang array dan juga kita bisa dengan aman menghindari menggunakan variabel sementara.
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)
Hanya untuk memiliki jari di pai. Di sini saya menyajikan implementasi rekursif shuffle Fisher Yates (saya pikir). Ini memberikan keacakan seragam.
Catatan: ~~
(operator tilde ganda) sebenarnya berperilaku seperti Math.floor()
untuk bilangan real positif. Hanya jalan pintas.
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])));
Sunting: Kode di atas adalah O (n ^ 2) karena penggunaan .splice()
tetapi kita bisa menghilangkan splice dan shuffle di O (n) dengan trik swap.
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");
Masalahnya, JS tidak bisa bekerja sama dengan rekursi besar. Dalam kasus khusus ini Anda ukuran array dibatasi dengan seperti 3000 ~ 7000 tergantung pada mesin browser Anda dan beberapa fakta yang tidak diketahui.
Mengacak array
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
untuk n seperti yang Anda <
tidak gunakan<=
arrayShuffle
fungsi terpendek
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;
}
Dari sudut pandang teoretis, cara paling elegan untuk melakukannya, menurut pendapat saya, adalah untuk mendapatkan nomor acak tunggal antara 0 dan n! -1 dan untuk menghitung pemetaan satu ke satu dari {0, 1, …, n!-1}
semua permutasi dari(0, 1, 2, …, n-1)
. Selama Anda dapat menggunakan generator acak (pseudo-) yang cukup andal untuk mendapatkan nomor seperti itu tanpa bias yang berarti, Anda memiliki informasi yang cukup di dalamnya untuk mencapai apa yang Anda inginkan tanpa memerlukan beberapa nomor acak lainnya.
Saat menghitung dengan angka mengambang presisi ganda IEEE754, Anda dapat mengharapkan generator acak Anda untuk memberikan sekitar 15 desimal. Karena Anda memiliki 15! = 1.307.674.368.000 (dengan 13 digit), Anda dapat menggunakan fungsi berikut dengan array yang berisi hingga 15 elemen dan menganggap tidak akan ada bias signifikan dengan array yang berisi hingga 14 elemen. Jika Anda bekerja pada masalah ukuran tetap yang mengharuskan untuk menghitung berkali-kali operasi acak ini, Anda mungkin ingin mencoba kode berikut yang mungkin lebih cepat daripada kode lain karena Math.random
hanya menggunakan sekali (namun melibatkan beberapa operasi penyalinan).
Fungsi berikut tidak akan digunakan, tetapi saya tetap memberikannya; itu mengembalikan indeks dari permutasi yang diberikan (0, 1, 2, …, n-1)
sesuai dengan pemetaan satu ke satu yang digunakan dalam pesan ini (yang paling alami ketika menghitung permuasi); itu dimaksudkan untuk bekerja dengan hingga 16 elemen:
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);
}
Kebalikan dari fungsi sebelumnya (diperlukan untuk pertanyaan Anda sendiri) ada di bawah ini; itu dimaksudkan untuk bekerja dengan hingga 16 elemen; mengembalikan permutasi urutan n dari (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;
}
Sekarang, yang Anda inginkan hanyalah:
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]; });
}
Ini harus bekerja hingga 16 elemen dengan sedikit bias teoritis (meskipun tidak terlihat dari sudut pandang praktis); dapat dilihat sebagai sepenuhnya dapat digunakan untuk 15 elemen; dengan array yang mengandung kurang dari 14 elemen, Anda dapat dengan aman mempertimbangkan sama sekali tidak ada bias.