Нам дан генератор случайных чисел, RandNum50
который генерирует случайное целое число равномерно в диапазоне 1–50. Мы можем использовать только этот генератор случайных чисел для генерации и печати всех целых чисел от 1 до 100 в случайном порядке. Каждое число должно приходить ровно один раз, и вероятность появления любого числа в любом месте должна быть одинаковой.
Какой алгоритм для этого наиболее эффективен?
algorithms
integers
randomness
random-number-generator
Радж Вадхва
источник
источник
RandNum100 = (RandNum50() * 2) - (RandNum50 > 25) ? 0 : 1)
.Ответы:
Я думал (так что может быть неправильно :-) этого решение , которое использует перетасовать Fisher-Yates . Для того , чтобы поддерживать равномерное распределение с хорошим приближением (смотрите раздел EDIT ниже) на каждой итерации вы можете использовать этот трюк , чтобы получить значение между и :0 k - 1O(N2) 0 k−1
krand
Алгоритм Фишера-Йейтса становится:
РЕДАКТИРОВАТЬ:
Как указал Эрик,m=⌈log2(k)⌉ r k
krand
вышеприведенная функция не возвращает действительно равномерное распределение. Есть и другие методы, которые можно использовать для получения лучшего (произвольно лучшего) и более быстрого приближения; но (насколько мне известно) единственный способ получить действительно равномерное распределение - это использовать выборку отклонения : выберите случайные биты и, если полученное число меньше верните его, в противном случае создайте другое случайное число; Возможная реализация:r kисточник
Поскольку другие люди дали приблизительные решения и решения, включающие получение неопределенного числа отклонений, как насчет доказательства того, что не существует такого алгоритма, который гарантированно требует только конечного числа
RandNum50()
вызовов?Как отмечали другие, печать чисел от 1 до 100 в случайном порядке эквивалентна печати случайной перестановки этих чисел; Есть 100! из этих перестановок, и поэтому любая конкретная перестановка должна быть выведена с вероятностью .1100!
Но если бы мы знали, что наш алгоритм использовал не более вызовов для некоторого , то мы могли бы рассуждать следующим образом: во-первых, выделите те вычислительные пути, которые делают меньше вызовов, чтобы сделать дополнительные фиктивные вызовы (то есть вызовы, где возвращаемое значение не имеет значения), так что все пути вычислений делают ровно вызовов. Любая заданная последовательность результатов наших вызовов должна приводить к некоторой выходной перестановке, и поэтому мы можем построить «таблицу результатов», которая отображает любую данную последовательность результатов наших вызовов в конкретный выходная перестановка. Так как каждый из этих результатов одинаково вероятен (каждый из них имеет вероятностьk k k k ( r 1 , r 2 , … , r k ) 1k k k k k (r1,r2,…,rk) гр150k ), тогда вероятность получения какой-либо конкретной перестановки из нашего алгоритма должна иметь вид для некоторого . Но может быть такой формы, потому чтоне делит для любого (например, 3 делит но не может делить любое число формы ). Это означает, что никакое возможное распределение результатов для вызовов случайных чисел не может привести к равномерной перестановке. с1c50k c 100! 50кк100! 50к1100! 100! 50k k 100! 50k
RandNum50
RandNum50
RandNum50
источник
Предыдущие решения не являются оптимальными. Сложность точно равна в вызовах RandNum50 и подробно описана здесь с использованием в качестве источника случайного бита (как предложено Vor):nlogn+O(1)
Основная идея заключается в том, что вы экономите много битов, если генерируете униформу от дои затем, используя факториальную базовую декомпозицию , вместо генерации последовательности униформ в диапазоне до , затем , затем и т. д., . Это на самом деле, как я уже упоминал в посте, тема статьи, которую я представил!п ! 1 2 3 н1 n! 1 2 3 n
Если вы не знаете, как сгенерировать униформу, как предлагается в этом посте, из случайного бита, вы также можете сгенерировать аппроксимацию униформы напрямую, таким образом (что эквивалентно «истинно» Вора, но быстрее):
идти так далеко, как вам нужно пойти. Это развивается в базе . Тогда просто усеченный , т.е. , в вашем случае, Это значение не является полностью случайным, но это мера однородности, которая часто используется. Или, как говорит Вор, вы можете отказаться , если . Затем с этим значением, вы можете сделать расширение факторных баз , как описаны в посте .50 P Q = PP 50 P Q=Pmodn n=100! P>n
источник
Я не сделал анализ , чтобы подтвердить , как равномерная (или нет) , это было бы, и она может быть скорректирована , чтобы быть истинным перетасовка, но вы могли бы просто выбрать из исходного массива из
i
го индекса =i + 1
, в(k + RandNum50() + RandNum50() - 1) mod (100 - k)
индекс, с удаление, дляk
= 0..99?Это «толкает» пик в
RandNum50() + RandNum50()
распределении вперед равномерно.Я уверен, что это не совсем верно, как я сказал это, потому что 0 индекс (1) не получается из первого выбора, и я не могу быстро увидеть альтернативную настройку 1..50 + 1..50, который производит 0 ..99.
Обновить
Чтобы устранить эту проблему , я отметил, я эффективно использовать
RandNum100
как уже упоминалось в комментариях вопрос к randomally Инициализируем первогоk
смещения.Это создает распределение со значительной волной на фронте.
Вместо того, чтобы продвигаться на 1, я использовал другой,
RandNum50
чтобы увеличить это первымk
. Это дает достаточно случайный для меня результат, но все же он не является «действительно» случайным, что можно легко увидеть, если изменить K на 2.Тестирование кода VB.NET где я обслужены любой даже K. Примечание оно представляет собой О (К), 6К + 2 в самом деле.
источник