Я просто повторю ответ Нила Баттерворта и укажу на некоторые проблемы с вашей первой идеей:
Вы предложили,
Перебирайте массив, скажем, 100 рази обмен случайного индекса с другим случайным индексом
Сделайте это строгим.Я предполагаю существование randn(int n)
, оболочки вокруг некоторого ГСЧ, производящей числа, равномерно распределенные в [0, n -1] и swap(int a[], size_t i, size_t j)
,
swap(int a[], size_t i, size_t j) {
int temp = a[i]; a[i] = a[j]; a[j] = temp;
}
который меняет a[i]
и a[j]
.Теперь давайте реализуем ваше предложение:
void silly_shuffle(size_t n, int a[n]) {
for (size_t i = 0; i < n; i++)
swap(a, randn(n), randn(n)); // swap two random elements
}
Обратите внимание, что это не лучше, чем эта более простая (но все же неправильная) версия:
void bad_shuffle(size_t n, int a[n]) {
for (size_t i = 0; i < n; i++)
swap(a, i, randn(n));
}
Ну, что не так?Подумайте, сколько перестановок дают вам эти функции: с n (или 2 × n для silly_shuffle
) случайного выбора в [0, n -1],код «честно» выберет один из n ² (или 2 × n ²) способов перетасовать колоду.Беда в том, что есть n != n × ( n -1) × ⋯ × 2 × 1 возможных расположений массива, и ни n ², ни 2 × n ² кратно n !, доказывая, что некоторые перестановки более вероятны, чем другие.
Перестановка Фишера-Йейтса фактически эквивалентна вашему второму предложению, только с некоторыми оптимизациями, которыеизменить (производительность = 0, сложность = серьезный) на (производительность = очень хорошо, сложность = довольно просто).(На самом деле, я не уверен, что существует более быстрая или простая правильная версия.)
void fisher_yates_shuffle(size_t n, int a[n]) {
for (size_t i = 0; i < n; i++)
swap(a, i, i+randn(n-1-i)); // swap element with random later element
}
ETA: см. Также этот пост на Coding Horror .