Saya memiliki array seperti ini:
var arr1 = ["a", "b", "c", "d"];
Bagaimana saya bisa mengacak / mengacaknya?
javascript
arrays
random
shuffle
Klik Upvote
sumber
sumber
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 )
Beberapa info lebih lanjut tentang algoritma yang digunakan.
sumber
i--
tidak--i
. Juga, tesif (i==0)...
ini berlebihan karena jikai == 0
itu sementara lingkaran akan pernah masuk. Panggilan keMath.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:
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)
.EDIT: Memperbarui ke ES6 / ECMAScript 2015
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.
sumber
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.sumber
Seseorang dapat (atau seharusnya) menggunakannya sebagai protoype dari Array:
Dari ChristopheD:
sumber
for...in
loop untuk beralih di atas array.Anda dapat melakukannya dengan mudah dengan memetakan dan mengurutkan:
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.
sumber
Gunakan perpustakaan underscore.js. Metode
_.shuffle()
ini bagus untuk kasus ini. Berikut ini adalah contoh dengan metode ini:sumber
shuffle
fungsi.BARU!
Algoritma shuffle Fisher-Yates lebih pendek & mungkin lebih cepat
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!)
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.)
sumber
fy
danshuffle prototype
, saya mendapatkanfy
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/3Sunting: 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:
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.
sumber
Andal, Efisien, Pendek
Beberapa solusi pada halaman ini tidak dapat diandalkan (mereka hanya mengacak sebagian array). Solusi lain secara signifikan kurang efisien. Dengan
testShuffleArrayFun
(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
ES6 Murni, Berulang
Uji Reliabilitas dan Kinerja
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).
Solusi Lain
Solusi lain hanya untuk bersenang-senang.
ES6 Murni, Rekursif
ES6 Pure menggunakan array.map
ES6 Pure menggunakan array.reduce
sumber
[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%.
sumber
var b =
dalam satu lingkaran daripada mendeklarasikan b di luar lingkaran dan menugaskannyab =
dalam satu lingkaran?Sunting: Jawaban ini salah
Lihat https://stackoverflow.com/a/18650169/28234 . Itu ditinggalkan di sini untuk referensi karena idenya tidak jarang.
https://javascript.info/task/shuffle
sumber
Dengan ES2015 Anda dapat menggunakan ini:
Pemakaian:
sumber
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, bukanshuffle
)Ini jsfiddle yang menunjukkan sedang digunakan .
sumber
[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 dianggap10
kurang dari2
karena1
kurang dari2
.Math.random()
menghasilkan. (yaitu, urutan leksikografis sama dengan urutan numerik ketika berhadapan dengan angka dari 0 (inklusif) hingga 1 (eksklusif))sumber
sumber
Anda dapat melakukannya dengan mudah dengan:
Silakan referensi di Array Penyortiran JavaScript
sumber
Solusi rekursif:
sumber
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.
sumber
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: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 karenaindexOf
berjalan dalam waktu O (n).sumber
Array.prototype.sort
melewati dua nilai sebagaia
danb
, bukan indeks. Jadi kode ini tidak berfungsi.fungsi acak yang tidak mengubah larik sumber
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 .
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:
sumber
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 ,
untuk contoh lebih lanjut, Anda dapat memeriksanya di sini
sumber
Belum lagi implementasi Fisher-Yates, menggunakan mode ketat:
sumber
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 memanfaatkanWeb Cryptography API
untuk tingkat kriptografi pengacakan .sumber
Solusi inline pendek modern menggunakan fitur ES6:
(untuk tujuan pendidikan)
sumber
Modifikasi sederhana dari jawaban CoolAJ86 yang tidak mengubah array asli:
sumber
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.
sumber
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 sepertiMath.floor()
untuk bilangan real positif. Hanya jalan pintas.Sunting: Kode di atas adalah O (n ^ 2) karena penggunaan
.splice()
tetapi kita bisa menghilangkan splice dan shuffle di O (n) dengan trik swap.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.
sumber
Mengacak array
sumber
-1
untuk n seperti yang Anda<
tidak gunakan<=
arrayShuffle
fungsi terpendeksumber
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: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)
:Sekarang, yang Anda inginkan hanyalah:
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.
sumber