После того, как Джон уже рассмотрел теорию , вот реализация:
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
возможным значениям where 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!
и, таким образом, приводит к неравномерному распределению, даже если алгоритм сортировки равномерно отображает перестановки. Если это имеет какое-либо практическое влияние, я не понимаю.
Настоящая проблема в том, что алгоритмы сортировки не гарантируют равномерного отображения на перестановки. Легко видеть, что Mergesort делает, поскольку он симметричен, но рассуждения о чем-то вроде Bubblesort или, что более важно, Quicksort или Heapsort - нет.
sort()
Итог : пока используется Mergesort, вы должны быть в достаточной безопасности, за исключением крайних случаев (по крайней мере, я надеюсь, что 2^c ≤ n!
это крайний случай), если нет, все ставки отключены.