Setelah Jon membahas teori ini , berikut ini implementasinya:
function shuffle(array) {
var tmp, current, top = array.length;
if(top) while(--top) {
current = Math.floor(Math.random() * (top + 1));
tmp = array[current];
array[current] = array[top];
array[top] = tmp;
}
return array;
}
Algoritma adalah O(n)
, sedangkan penyortiran seharusnya O(n log n)
. Bergantung pada overhead dari mengeksekusi kode JS dibandingkan dengan sort()
fungsi asli , ini dapat menyebabkan perbedaan yang nyata dalam kinerja yang harus meningkat dengan ukuran array.
Dalam komentar untuk jawaban bobobobo , saya menyatakan bahwa algoritma yang dimaksud mungkin tidak menghasilkan probabilitas yang terdistribusi secara merata (tergantung pada implementasinya sort()
).
Argumen saya sejalan dengan ini: Algoritma pengurutan memerlukan sejumlah c
perbandingan, misalnya c = n(n-1)/2
untuk Bubblesort. Fungsi perbandingan acak kami membuat hasil masing-masing perbandingan sama kemungkinannya, yaitu ada hasil yang 2^c
sama - sama memungkinkan . Sekarang, setiap hasil harus sesuai dengan salah satu n!
permutasi dari entri array, yang membuat pemerataan tidak mungkin dalam kasus umum. (Ini adalah penyederhanaan, karena jumlah aktual perbandingan yang dibutuhkan tergantung pada larik input, tetapi pernyataan tersebut harus tetap berlaku.)
Seperti yang ditunjukkan oleh Jon, ini saja bukan alasan untuk lebih memilih Fisher-Yates daripada menggunakan sort()
, karena generator angka acak juga akan memetakan sejumlah terbatas nilai pseudo-acak ke n!
permutasi. Tetapi hasil Fisher-Yates masih harus lebih baik:
Math.random()
menghasilkan nomor pseudo-acak dalam kisaran [0;1[
. Karena JS menggunakan nilai floating point presisi ganda, ini sesuai dengan 2^x
nilai yang mungkin ada di mana 52 ≤ x ≤ 63
(saya terlalu malas untuk menemukan angka aktual). Distribusi probabilitas yang dihasilkan menggunakan Math.random()
akan berhenti berperilaku baik jika jumlah peristiwa atom adalah sama besarnya.
Saat menggunakan Fisher-Yates, parameter yang relevan adalah ukuran array, yang tidak boleh didekati 2^52
karena keterbatasan praktis.
Saat menyortir dengan fungsi perbandingan acak, fungsi ini pada dasarnya hanya peduli jika nilai kembali positif atau negatif, jadi ini tidak akan menjadi masalah. Tetapi ada yang serupa: Karena fungsi perbandingan berperilaku baik, 2^c
hasil yang mungkin, sebagaimana dinyatakan, sama-sama kemungkinan. Jika c ~ n log n
kemudian di 2^c ~ n^(a·n)
mana a = const
, yang paling tidak memungkinkan yang 2^c
besarnya sama dengan (atau bahkan kurang dari) n!
dan dengan demikian mengarah pada distribusi yang tidak rata, bahkan jika algoritma pengurutan tempat memetakan ke permutasi secara merata. Jika ini memiliki dampak praktis ada di luar saya.
Masalah sebenarnya adalah bahwa algoritma pengurutan tidak dijamin untuk memetakan ke permutasi secara merata. Sangat mudah untuk melihat bahwa Mergesort melakukan apa yang simetris, tetapi alasan tentang sesuatu seperti Bubblesort atau, yang lebih penting, Quicksort atau Heapsort, tidak.
Intinya: Selama sort()
menggunakan Mergesort, Anda harus cukup aman kecuali dalam kasus sudut (setidaknya saya berharap itu 2^c ≤ n!
kasus sudut), jika tidak, semua taruhan dibatalkan.