Після того, як Джон уже висвітлив цю теорію , ось реалізація:
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;
}
Алгоритм є O(n)
, тоді як сортування має бути O(n log n)
. Залежно від накладних витрат виконання JS-коду порівняно з нативною sort()
функцією, це може призвести до помітної різниці в продуктивності, яка повинна збільшуватися з розмірами масиву.
У коментарях до відповіді bobobobo я заявив, що алгоритм, про який йдеться, може не створювати рівномірно розподілених ймовірностей (залежно від реалізації sort()
).
Мій аргумент іде в наступному напрямку: Алгоритм сортування вимагає певної кількості c
порівнянь, наприклад, c = n(n-1)/2
для Bubblesort. Наша функція випадкового порівняння робить результат кожного порівняння однаково вірогідним, тобто є 2^c
однаково ймовірні результати. Тепер кожен результат повинен відповідати одній із n!
перестановок записів масиву, що робить рівномірним розподіл неможливим у загальному випадку. (Це спрощення, оскільки фактична кількість необхідних порівнянь залежить від вхідного масиву, але твердження все одно має бути виконане.)
Як вказував Джон, саме по собі це не є підставою віддавати перевагу використанню Фішера-Йейтса sort()
, оскільки генератор випадкових чисел також буде відображати кінцеве число псевдовипадкових значень n!
перестановок. Але результати Фішера-Йейта все-таки повинні бути кращими:
Math.random()
виробляє псевдовипадкове число в діапазоні [0;1[
. Оскільки JS використовує подвійну точність значень з плаваючою точкою, це відповідає 2^x
можливим значенням, де 52 ≤ x ≤ 63
(я лінивий знайти фактичне число). Розподіл ймовірностей, згенерований за допомогою Math.random()
, перестане вести себе добре, якщо кількість атомних подій буде однакового порядку.
При використанні Fisher-Yates відповідним параметром є розмір масиву, який ніколи не повинен наближатися 2^52
через практичні обмеження.
При сортуванні за допомогою функції випадкового порівняння функція в основному дбає лише, якщо повернене значення є позитивним чи негативним, тому це ніколи не буде проблемою. Але є аналогічний: Оскільки функція порівняння добре поводиться, 2^c
можливі результати, як було сказано, однаково вірогідні. Якщо c ~ n log n
тоді, 2^c ~ n^(a·n)
де a = const
, що робить принаймні можливим, що 2^c
має таку ж величину, як (або навіть менше), n!
і, таким чином, веде до нерівномірного розподілу, навіть якщо алгоритм сортування, де відображати перестановки рівномірно. Якщо це має якийсь практичний вплив, це поза мною.
Справжня проблема полягає в тому, що алгоритми сортування не гарантують рівномірне відображення перестановок. Неважко помітити, що Мергезорт виглядає як симетричний, але міркування про щось на зразок Bubblesort або, що ще важливіше, Quicksort або Heapsort, це не так.
Суть: Якщо ви sort()
користуєтеся Mergesort, ви повинні бути в безпеці, за винятком випадків кутового (принаймні, я сподіваюся, що 2^c ≤ n!
це кутовий випадок), якщо ні, всі ставки відключені.