Для функции, которая выдает случайное целое число в диапазоне от 1 до 5, напишите функцию, которая выдает случайное целое число в диапазоне от 1 до 7.
- Что такое простое решение?
- Каково эффективное решение для уменьшения использования памяти или работы на более медленном процессоре?
7 * rand5() / 5
?Ответы:
Это эквивалентно решению Адама Розенфилда, но может быть немного более понятным для некоторых читателей. Предполагается, что rand5 () - это функция, которая возвращает статистически случайное целое число в диапазоне от 1 до 5 включительно.
Как это работает? Подумайте об этом так: представьте себе распечатку этого массива двойного размера на бумаге, прикрепление его к доске для дротиков и случайное бросание в нее дротиков. Если вы нажмете ненулевое значение, это статистически случайное значение от 1 до 7, так как есть равное количество ненулевых значений на выбор. Если вы попали в ноль, просто продолжайте бросать дротик, пока не достигнете ненулевого значения. Вот что делает этот код: индексы i и j случайным образом выбирают место на доске для дартса, и если мы не получим хорошего результата, мы продолжаем бросать дротики.
Как сказал Адам, в худшем случае это может продолжаться вечно, но статистически наихудшего случая никогда не бывает. :)
источник
rand5
одинаково, каждая ячейка вvals
сетке имеет равную вероятность быть выбранной. Сетка содержит ровно три копии каждого целого числа в интервале [1, 7], плюс четыре нуля. Таким образом, «сырой» поток результатов имеет тенденцию к четной смеси значений [1, 7], плюс некоторые нули, которые встречаются чуть чаще, чем любое отдельное допустимое значение. Но это не имеет значения, потому что нули удаляются, оставляя лишь четную смесь значений [1, 7].Не существует (абсолютно правильного) решения, которое будет выполняться за постоянное количество времени, поскольку 1/7 - это бесконечное десятичное число в базе 5. Одним из простых решений будет использование выборки отклонения, например:
Ожидаемое время выполнения составляет 25/21 = 1,19 итераций цикла, но существует бесконечно малая вероятность зацикливания навсегда.
источник
N
вызовыrand5()
в худшем случае. Тогда есть 5 ^ N возможных результатов последовательности вызововrand5
, каждый из которых имеет выход 1-7. Таким образом, если вы сложите все возможные последовательности вызовов, выход которыхk
для каждого 1≤k≤7, то вероятность того, что результатомk
будет m / 5 ^ N, где m - количество таких последовательностей. Итак, m / 5 ^ N = 1/7, но нет возможных целочисленных решений (N, m) этого ==> противоречия.Я хотел бы добавить еще один ответ, в дополнение к моему первому ответу . Этот ответ пытается минимизировать количество вызовов на
rand5()
один вызовrand7()
, чтобы максимально использовать случайность. То есть, если вы считаете случайность ценным ресурсом, мы хотим использовать как можно большую ее часть, не выбрасывая случайные биты. Этот ответ также имеет некоторые сходства с логикой, представленной в ответе Ивана .Энтропия случайной величины является хорошо определенной величиной. Для случайной величины, которая принимает N состояний с равными вероятностями (равномерное распределение), энтропия равна log 2 Н. Таким образом, она
rand5()
имеет приблизительно 2,332193 бита энтропии иrand7()
имеет приблизительно 2,80735 бита энтропии. Если мы надеемся максимизировать наше использование случайности, нам нужно использовать все 2.32193 бита энтропии при каждом вызовеrand5()
и применять их для генерации 2.80735 бита энтропии, необходимой для каждого вызоваrand7()
. Таким образом, фундаментальное ограничение заключается в том, что мы можем делать не лучше, чем log (7) / log (5) = 1.20906 вызовов наrand5()
один вызовrand7()
.Примечания: все логарифмы в этом ответе будут основанием 2, если не указано иное.
rand5()
предполагается, что возвращаются числа в диапазоне [0, 4], иrand7()
предполагается, что возвращаются числа в диапазоне [0, 6]. Настройка диапазонов на [1, 5] и [1, 7] соответственно тривиальна.Так как нам это сделать? Мы генерируем бесконечно точное случайное действительное число от 0 до 1 (представьте, что мы действительно можем вычислить и сохранить такое бесконечно точное число - мы исправим это позже). Мы можем сгенерировать такое число, генерируя его цифры в базе 5: мы выбираем случайное число 0.
a
1a
2a
3 ..., где каждая цифра ai
выбирается вызовомrand5()
. Например, если наш RNG выбрал ai
= 1 для всехi
, тогда игнорируя тот факт, что это не очень случайно, это будет соответствовать действительному числу 1/5 + 1/5 2 + 1/5 3 + ... = 1/4 (сумма геометрического ряда).Итак, мы выбрали случайное действительное число от 0 до 1. Теперь я утверждаю, что такое случайное число распределено равномерно. Интуитивно понятно, что это легко понять, поскольку каждая цифра выбрана одинаково, а число является бесконечно точным. Однако формальное доказательство этого несколько сложнее, поскольку теперь мы имеем дело с непрерывным распределением, а не с дискретным распределением, поэтому нам нужно доказать, что вероятность того, что наше число лежит в интервале [
a
,b
], равна длине этот интервалb - a
. Доказательство оставлено в качестве упражнения для читателя =).Теперь, когда у нас есть случайное действительное число, выбранное равномерно из диапазона [0, 1], нам нужно преобразовать его в серию равномерно случайных чисел в диапазоне [0, 6], чтобы сгенерировать вывод
rand7()
. как нам это сделать? Как раз наоборот, что мы только что сделали - мы конвертируем его в бесконечно точный десятичный знак в базе 7, и тогда каждая цифра в базовой 7 будет соответствовать одному выводуrand7()
.Взяв пример из предыдущего, если наша
rand5()
производит бесконечный поток 1, то наше случайное действительное число будет 1/4. Преобразовав 1/4 в основание 7, мы получим бесконечное десятичное число 0,15151515 ..., поэтому мы получим в качестве выходных 1, 5, 1, 5, 1, 5 и т. Д.Итак, у нас есть основная идея, но у нас осталось две проблемы: мы не можем на самом деле вычислить или сохранить бесконечно точное действительное число, так как же нам иметь дело только с его конечной частью? Во-вторых, как мы на самом деле конвертируем его в базу 7?
Один из способов преобразования числа от 0 до 1 в основание 7 заключается в следующем:
Чтобы решить проблему бесконечной точности, мы вычисляем частичный результат и сохраняем верхнюю границу того, каким может быть результат. То есть, предположим, мы звонили
rand5()
дважды, и он возвращал 1 оба раза. Число, которое мы сгенерировали до сих пор, составляет 0,11 (основание 5). Независимо от того, какую оставшуюся часть бесконечной серии вызовов нужноrand5()
произвести, генерируемое случайное число никогда не будет больше 0,12: всегда верно, что 0,11 ≤ 0,11xyz ... <0,12.Таким образом, отслеживая текущее число и максимальное значение, которое оно может когда-либо принять, мы конвертируем оба числа в основание 7. Если они согласуются с первыми
k
цифрами, то мы можем безопасно вывести следующиеk
цифры - независимо от того, что бесконечный поток из базовых 5 цифр, они никогда не повлияют на следующиеk
цифры в базовом 7 представлении!И это алгоритм - чтобы сгенерировать следующий вывод
rand7()
, мы генерируем только столько цифр,rand5()
сколько нам нужно, чтобы гарантировать, что мы точно знаем значение следующей цифры при преобразовании случайного действительного числа в основание 7. Здесь реализация Python с тестовым набором:Обратите внимание, что
rand7_gen()
возвращается генератор, поскольку он имеет внутреннее состояние, включающее преобразование числа в основание 7. Испытательный комплект вызываетnext(r7)
10000 раз, чтобы получить 10000 случайных чисел, а затем измеряет их распределение. Используется только целочисленная математика, поэтому результаты в точности верны.Также обратите внимание, что числа здесь становятся очень большими, очень быстрыми. Способности 5 и 7 растут быстро. Следовательно, производительность начнет заметно ухудшаться после генерации большого количества случайных чисел из-за арифметики Бигнума. Но помните здесь, моя цель состояла в том, чтобы максимально использовать случайные биты, а не максимизировать производительность (хотя это вторичная цель).
За один прогон этого я сделал 12091 вызов
rand5()
на 10000 вызововrand7()
, достигнув минимума вызовов log (7) / log (5) в среднем до 4 значащих цифр, и полученный результат был равномерным.Чтобы перенести этот код на язык, в котором нет встроенных произвольно больших целых чисел, вам нужно ограничить значения
pow5
иpow7
максимальное значение вашего собственного целочисленного типа - если они становятся слишком большими, затем выполнить сброс все и начать все сначала. Это немного увеличит среднее количество вызовов наrand5()
один вызовrand7()
, но, надеюсь, оно не должно увеличиться слишком сильно даже для 32- или 64-разрядных целых чисел.источник
(Я украл ответ Адама Розенфельда и заставил его работать примерно на 7% быстрее.)
Предположим, что rand5 () возвращает один из {0,1,2,3,4} с равным распределением, и цель - вернуть {0,1,2,3,4,5,6} с равным распределением.
Мы отслеживаем наибольшее значение, которое цикл может сделать в переменной
max
. Если результат пока находится между max% 7 и max-1, то результат будет равномерно распределен в этом диапазоне. Если нет, мы используем остаток, который является случайным между 0 и max% 7-1, и еще один вызов rand (), чтобы создать новое число и новый максимум. Тогда мы начнем снова.Изменить: Ожидайте, сколько раз вызов rand5 () равен x в этом уравнении:
источник
5 * rand5() + rand5()
.Алгоритм:
7 может быть представлен в последовательности из 3 бит
Используйте rand (5) для случайного заполнения каждого бита 0 или 1.
Например, для вызова rand (5) и
если результат 1 или 2, заполните бит 0,
если результат 4 или 5, заполните бит 1,
если результат 3, затем проигнорируйте и сделайте это снова (отклонение)
Таким образом, мы можем случайным образом заполнить 3 бита 0/1 и получить число от 1 до 7.
РЕДАКТИРОВАТЬ: кажется, самый простой и эффективный ответ, поэтому вот код для этого:
источник
источник
Изменить: это не совсем работает. Это примерно на 2 части из 1000 (при условии идеального ранда5). Ведра получают:
Переходя на сумму
кажется, получает порядок величины для каждых 2 добавленных
Кстати, приведенная выше таблица ошибок была сгенерирована не с помощью выборки, а с помощью следующего отношения повторения:
источник
источник
ans += (r < 3) << i
Ниже приводится равномерное распределение в {1, 2, 3, 4, 5, 6, 7} с использованием генератора случайных чисел, создающего равномерное распределение в {1, 2, 3, 4, 5}. Код грязный, но логика понятна.
источник
В отличие от выбранного решения, алгоритм будет работать в постоянное время. Однако он делает на 2 вызова больше, чем среднее время выполнения выбранного решения.
Обратите внимание, что этот генератор не идеален (число 0 имеет на 0,0064% больше шансов, чем любое другое число), но для большинства практических целей гарантия постоянного времени, вероятно, перевешивает эту неточность.
объяснение
Это решение основано на том факте, что число 15 624 делится на 7, и, таким образом, если мы можем случайным образом и равномерно генерировать числа от 0 до 15 624, а затем взять мод 7, мы можем получить почти равномерный генератор rand7. Числа от 0 до 15 624 можно сгенерировать равномерно, выполнив rand5 6 раз и используя их для формирования цифр базового числа 5 следующим образом:
Однако свойства мода 7 позволяют немного упростить уравнение:
Так
становится
теория
Число 15 624 не было выбрано случайно, но его можно обнаружить с помощью маленькой теоремы Ферма, которая утверждает, что если p - простое число, то
Так что это дает нам,
(5 ^ 6) -1 равно
Это число в форме основания 5, и поэтому мы можем видеть, что этот метод может использоваться для перехода от любого генератора случайных чисел к любому другому генератору случайных чисел. Хотя небольшое смещение в сторону 0 всегда вводится при использовании показателя степени p-1.
Чтобы обобщить этот подход и быть более точным, мы можем иметь такую функцию:
источник
Здесь разрешены домашние задания?
Эта функция выполняет математику «базовый 5» для генерации числа от 0 до 6.
источник
Если мы рассмотрим дополнительное ограничение попытки дать наиболее эффективный ответ, т. Е. Тот, который задан входным потоком,
I
из равномерно распределенных целых чисел длинойm
от 1-5, выводится потокO
, из равномерно распределенных целых чисел от 1-7 самой длинной длины относительно чтобыm
, скажемL(m)
.Простейший способ проанализировать это состоит в том, чтобы рассматривать потоки I и
O
как 5-ти и 7-ти чисел соответственно. Это достигается с помощью идеи основного ответа о потокеa1, a2, a3,... -> a1+5*a2+5^2*a3+..
и аналогично для потокаO
.Затем, если мы возьмем часть входного потока длины,
m choose n s.t. 5^m-7^n=c
гдеc>0
и как можно меньше. Затем есть единообразное отображение из входного потока длиной m в целые числа от1
to5^m
и другое равномерное отображение из целых чисел от 17^n
до в выходной поток длины n, где нам может потребоваться проиграть несколько случаев из входного потока при отображении целого числа превышает7^n
.Таким образом, это дает значение для
L(m)
околоm (log5/log7)
которого примерно.82m
.Сложность вышеуказанного анализа заключается в уравнении,
5^m-7^n=c
которое непросто решить точно, и в случае, когда равномерное значение от1
к5^m
превышает,7^n
и мы теряем эффективность.Вопрос в том, насколько близко может быть достигнуто наилучшее возможное значение m (log5 / log7). Например, когда это число приближается к целому числу, можем ли мы найти способ достижения этого точного целого числа выходных значений?
Если
5^m-7^n=c
затем из входного потока мы эффективно генерируем равномерное случайное число из0
в(5^m)-1
и не используем никаких значений выше, чем7^n
. Однако эти значения могут быть восстановлены и использованы снова. Они эффективно генерируют единую последовательность чисел от 1 до5^m-7^n
. Таким образом, мы можем затем попытаться использовать их и преобразовать их в 7-разрядные числа, чтобы мы могли создать больше выходных значений.Если мы допустим,
T7(X)
чтобы быть средней длиной выходной последовательностиrandom(1-7)
целых чисел, полученных из равномерного ввода размераX
, и предполагая, что5^m=7^n0+7^n1+7^n2+...+7^nr+s, s<7
.Тогда,
T7(5^m)=n0x7^n0/5^m + ((5^m-7^n0)/5^m) T7(5^m-7^n0)
поскольку у нас нет длины последовательности с вероятностью 7 ^ n0 / 5 ^ m с остатком длины5^m-7^n0
с вероятностью(5^m-7^n0)/5^m)
.Если мы просто продолжим замену, мы получим:
следовательно
Другой способ выразить это:
Наилучший возможный случай - мой оригинальный выше, где
5^m=7^n+s
, гдеs<7
.Тогда
T7(5^m) = nx(7^n)/(7^n+s) = n+o(1) = m (Log5/Log7)+o(1)
как прежде.Худший случай, когда мы можем найти только k и st 5 ^ m = kx7 + s.
Другие случаи находятся где-то посередине. Было бы интересно посмотреть, насколько хорошо мы можем сделать для очень большого m, т.е. насколько хорошо мы можем получить термин ошибки:
В
e(m) = o(1)
целом, кажется невозможным добиться этого, но, надеюсь, мы сможем доказатьe(m)=o(m)
.Тогда все дело в распределении 7-ми цифр
5^m
для различных значенийm
.Я уверен, что есть много теории, которая покрывает это, я могу взглянуть и доложить в какой-то момент.
источник
Вот рабочая реализация ответа Адама на Python .
Мне нравится бросать алгоритмы, которые я смотрю, в Python, чтобы я мог поиграть с ними, подумал, что я опубликую это здесь в надежде, что это будет полезно для кого-то там, а не то, что это заняло много времени, чтобы бросить вместе.
источник
rand5()
это приличный PRNG, то цикл не будет бесконечным, потому что в конечном итоге5*(rand5() - 1) + rand5()
определенно будет <= 21.)Почему бы не сделать это просто?
Вероятность получить 1 и 7 в этом решении ниже по модулю, однако, если вы просто хотите быстрое и удобочитаемое решение, это путь.
источник
Предполагая, что rand (n) здесь означает «случайное целое число в равномерном распределении от 0 до n-1 », вот пример кода с использованием randint Python, который имеет такой эффект. Он использует только randint (5) и константы, чтобы произвести эффект randint (7) . Немного глупо, на самом деле
источник
do ... while
. Это могло быть1337
, или12345
, или любое число> 1.Предпосылка Адама Розенфилда заключается в следующем:
Когда n равно 2, у вас есть 4 возможности выбрасывания: y = {22, 23, 24, 25}. Если вы используете n равное 6, у вас есть только 1 выбрасывание: y = {15625}.
5 ^ 6 = 15625
7 * 2232 = 15624
Вы звоните rand5 больше раз. Однако у вас гораздо меньше шансов получить одноразовое значение (или бесконечный цикл). Если есть способ получить возможное выбрасываемое значение для y, я еще не нашел его.
источник
Вот мой ответ:
Это немного сложнее, чем другие, но я считаю, что это минимизирует количество вызовов rand5. Как и в случае с другими решениями, существует небольшая вероятность того, что он может зацикливаться в течение длительного времени.
источник
Просто и эффективно:
(Вдохновленный тем, какой ваш любимый мультфильм "программист"? ).
источник
Пока не осталось семи вариантов выбора, нарисуйте другое случайное число, которое умножает количество возможностей на пять. В Perl:
источник
$possibilities
всегда нужно расти до 25, чтобы выйти из цикла и вернуться. Итак, ваш первый результат -[0-124] % 7
неравномерно распределенный, потому что125 % 7 != 0
(на самом деле это 6).Мне не нравятся диапазоны, начинающиеся с 1, поэтому я начну с 0 :-)
источник
from collections import defaultdict def r7(n): if not n: yield [] else: for i in range(1, 6): for j in r7(n-1): yield [i] + j def test_r7(): d = defaultdict(int) for x in r7(6): s = (((((((((x[5] * 5) + x[4]) * 5) + x[3]) * 5) + x[2]) * 5) + x[1]) * 5) + x[0] if s <= 15623: d[s % 7] += 1 print d
Вот и все, равномерное распределение и ноль вызовов rand5.
Нужно заранее установить семена.
источник
Я знаю, что на него ответили, но, кажется, это работает нормально, но я не могу сказать вам, есть ли у него предвзятость. Мое «тестирование» предполагает, что это, по крайней мере, разумно.
Возможно, Адам Розенфилд будет достаточно любезен, чтобы прокомментировать?
Моя (наивная?) Идея такова:
Накапливайте rand5, пока не будет достаточно случайных битов, чтобы создать rand7. Это занимает не более 2 рандов. Для получения номера rand7 я использую накопленное значение mod 7.
Чтобы избежать переполнения аккумулятора, и так как аккумулятор является модом 7, тогда я беру мод 7 аккумулятора:
Функция rand7 () выглядит следующим образом:
(Я позволил диапазону rand5 быть 0-4 и rand7 также 0-6.)
Изменить: Добавлены результаты для 100 миллионов испытаний.
«Реальные» функции ранда 5 или 7
rand5: avg = 1.999802 0: 20003944 1: 19999889 2: 20003690 3: 19996938 4: 19995539 rand7: avg = 3.000111 0: 14282851 1: 14282879 2: 14284554 3: 14288546 4: 14292388 5: 14288736 6: 14280046
Мой ранд7
Среднее выглядит нормально, а распределение чисел тоже хорошо.
randt: avg = 3.000080 0: 14288793 1: 14280135 2: 14287848 3: 14285277 4: 14286341 5: 14278663 6: 14292943
источник
Есть элегантные алгоритмы, упомянутые выше, но вот один из способов приблизиться к этому, хотя это может быть обходным. Я предполагаю значения, сгенерированные из 0.
R2 = генератор случайных чисел, дающий значения меньше 2 (пробное пространство = {0, 1})
R8 = генератор случайных чисел, дающий значения меньше 8 (пробное пространство = {0, 1, 2, 3, 4, 5, 6, 7 })
Чтобы сгенерировать R8 из R2, вы будете запускать R2 трижды и использовать объединенный результат всех 3 запусков как двоичное число с 3 цифрами. Вот диапазон значений, когда R2 запускается трижды:
0 0 0 -> 0
.
,
1 1 1 -> 7
Теперь, чтобы сгенерировать R7 из R8, мы просто запускаем R7 снова, если он возвращает 7:
Обходным решением является создание R2 из R5 (точно так же, как мы создали R7 из R8), затем R8 из R2 и затем R7 из R8.
источник
Вот решение, которое полностью соответствует целым числам и находится в пределах примерно 4% от оптимального (то есть использует 1,26 случайных чисел в {0..4} для каждого из {0..6}). Код написан на Scala, но математика должна быть достаточно понятной на любом языке: вы используете тот факт, что 7 ^ 9 + 7 ^ 8 очень близок к 5 ^ 11. Таким образом, вы выбираете 11-значное число в базе 5, а затем интерпретируете его как 9-значное число в базе 7, если оно находится в диапазоне (задает 9-значное 7-значное число), или как 8-значное число, если оно превышает 9-значное число, и т. Д. .:
Если вы вставите тест в интерпретатор (на самом деле REPL), вы получите:
Распределение хорошее и плоское (в пределах примерно 10k от 1/7 от 10 ^ 8 в каждом бине, как и следовало ожидать из приблизительно гауссовского распределения).
источник
Используя скользящий итог , вы можете оба
Обе эти проблемы являются проблемой с
rand(5)+rand(5)...
решениями упрощенного типа. Следующий код Python показывает, как его реализовать (большая часть этого - доказательство распространения).И этот вывод показывает результаты:
Упрощенно
rand(5)+rand(5)
, игнорируя те случаи, когда возвращается более 6, типичное отклонение составляет 18%, что в 100 раз больше, чем у метода, показанного выше:И, по совету Nixuz, я очистил скрипт, чтобы вы могли просто извлечь и использовать
rand7...
материал:источник
Этот ответ является скорее экспериментом по получению максимально возможной энтропии из функции Rand5. Поэтому он несколько неясен и почти наверняка намного медленнее, чем другие реализации.
Предполагая равномерное распределение от 0 до 4 и получающееся в результате равномерное распределение от 0 до 6:
Количество битов, добавляемых в буфер за вызов к Rand5, в настоящее время составляет 4/5 * 2, то есть 1,6. Если включено значение вероятности 1/5, то оно увеличивается на 0,05, то есть на 1,65, но см. Комментарий в коде, где мне пришлось отключить это.
Биты, потребляемые при вызове Rand7 = 3 + 1/8 * (3 + 1/8 * (3 + 1/8 * (...
Это 3 + 3/8 + 3/64 + 3/512 ... так) около 3,42
Извлекая информацию из семерки, я восстанавливаю 1/8 * 1/7 бит на вызов, так что около 0,018
Это дает чистое потребление 3,4 бита на вызов, что означает, что соотношение составляет 2,125 вызовов к Rand5 для каждого Rand7. Оптимальным должно быть 2.1.
Я полагаю, что этот подход значительно медленнее, чем многие другие здесь, если только стоимость звонка в Rand5 не слишком высока (скажем, вызов какого-то внешнего источника энтропии).
источник
в php
зацикливается, чтобы получить случайное число от 16 до 127, делится на шестнадцать, чтобы создать число от 1 до 7,9375, затем округляется, чтобы получить целое число от 1 до 7. Если я не ошибаюсь, есть шанс получить 16/112 любой из 7 результатов.
источник
источник
7 = 111b
сp(7) = 8 / 125
Я думаю, что у меня есть четыре ответа, два из которых дают точные решения, подобные @Adam Rosenfield, но без проблемы бесконечного цикла, и два других с почти идеальным решением, но более быстрой реализацией, чем первый.
Лучшее точное решение требует 7 вызовов
rand5
, но давайте продолжим, чтобы понять.Метод 1 - Точный
Сила ответа Адама в том, что он дает идеальное равномерное распределение, и существует очень высокая вероятность (21/25), что понадобятся только два вызова rand5 (). Однако в худшем случае это бесконечный цикл.
Первое решение ниже также дает идеальное равномерное распределение, но требует в общей сложности 42 вызовов
rand5
. Нет бесконечных петель.Вот реализация R:
Для людей, не знакомых с R, вот упрощенная версия:
Распределение
rand5
будет сохранено. Если мы посчитаем, каждая из 7 итераций цикла имеет 5 ^ 6 возможных комбинаций, таким образом, общее количество возможных комбинаций равно(7 * 5^6) %% 7 = 0
. Таким образом, мы можем разделить случайные числа, сгенерированные на равные группы по 7. Смотрите метод 2 для более подробного обсуждения этого.Вот все возможные комбинации:
Я думаю, прямо сейчас показать, что метод Адама будет работать намного быстрее. Вероятность того, что
rand5
в решении Адама есть 42 или более вызовов , очень мала ((4/25)^21 ~ 10^(-17)
).Способ 2 - не точно
Теперь второй метод, который почти одинаков, но требует 6 вызовов
rand5
:Вот упрощенная версия:
Это, по сути, одна итерация метода 1. Если мы сгенерируем все возможные комбинации, вот результат:
Один номер появится еще раз в
5^6 = 15625
испытаниях.Теперь, в методе 1, добавив от 1 до 6, мы перемещаем число 2233 в каждую последующую точку. Таким образом, общее количество комбинаций будет совпадать. Это работает, потому что 5 ^ 6 %% 7 = 1, а затем мы делаем 7 подходящих вариантов, поэтому (7 * 5 ^ 6 %% 7 = 0).
Метод 3 - Точный
Если аргумент метода 1 и 2 понятен, метод 3 следует и требует только 7 вызовов
rand5
. На данный момент, я чувствую, что это минимальное количество звонков, необходимое для точного решения.Вот реализация R:
Для людей, не знакомых с R, вот упрощенная версия:
Распределение
rand5
будет сохранено. Если мы посчитаем, у каждой из 7 итераций цикла будет 5 возможных результатов, то есть общее количество возможных комбинаций(7 * 5) %% 7 = 0
. Таким образом, мы можем разделить случайные числа, сгенерированные на равные группы по 7. Смотрите метод один и два для более подробного обсуждения этого.Вот все возможные комбинации:
Я думаю, прямо сейчас показать, что метод Адама все еще будет работать быстрее. Вероятность того, что
rand5
в решении Адама будет 7 или более вызовов , все еще мала ((4/25)^3 ~ 0.004
).Метод 4 - не точно
Это незначительная вариация второго метода. Это почти равномерно, но требует 7 вызовов
rand5
, то есть один дополнительный метод 2:Вот упрощенная версия:
Если мы сгенерируем все возможные комбинации, вот результат подсчета:
Два числа появятся один раз меньше в
5^7 = 78125
испытаниях. Для большинства целей я могу жить с этим.источник
i=7
также не имеет никакого эффекта, так как добавление7*rand5()
вr
не меняет значениеr
мода 7.)Вам нужна функция rand1_7 () , я написал rand1_5 (), чтобы вы могли ее протестировать и построить.
источник