Генерация двоичного десятичного числа. Вместо того, чтобы хранить это явно, просто следите за минимальными и максимальными возможными значениями. Как только эти значения лежат в одном и том же целом числе, вернуть это целое число. Эскиз кода ниже.
(Изменить) Более полное объяснение: допустим, вы хотите сгенерировать случайное целое число от 1 до 3 включительно с вероятностью 1/3 каждого. Мы делаем это путем генерации случайного двоичного десятичного вещественного числа x в диапазоне (0, 1). Если x <1/3, вернуть 1, иначе, если x <2/3, вернуть 2, иначе вернуть 3. Вместо того, чтобы явно генерировать цифры для x, мы просто отслеживаем минимальные и максимально возможные значения x. Первоначально минимальное значение x равно 0, а максимальное равно 1. Если вы сначала перевернете головки, то первая цифра x за десятичной точкой (в двоичном формате) будет 1. Минимально возможное значение x (в двоичном формате) станет 0,100000. = 1/2, а максимальное значение равно 0,111111111 = 1. Теперь, если ваш следующий бросок - это хвосты, x начинается с 0,10. Минимальное возможное значение составляет 0,1000000 = 1/2, а максимальное - 0,1011111 = 3/4. Минимально возможное значение x равно 1/2, так что вы знаете, что нет шансов вернуть 1, так как для этого требуется х <1/3. Вы все равно можете вернуть 2, если х в конечном итоге будет 1/2 <x <2/3 или 3, если 2/3 <x <3/4. Теперь предположим, что третий бросок - это хвосты. Тогда х должен начинаться с 0,100. Мин = 0,10000000 = 1/2 и макс = 0,100111111 = 5/8. Теперь, поскольку 1/3 <1/2 <5/8 <2/3, мы знаем, что x должен попадать в интервал (1/3, 2/3), поэтому мы можем прекратить генерировать цифры x и просто вернуть 2.
Код делает это по существу, за исключением того, что вместо генерации x между 0 и 1 он генерирует x между a и b, но принцип тот же.
def gen(a, b):
min_possible = a
max_possible = b
while True:
floor_min_possible = floor(min_possible)
floor_max_possible = floor(max_possible)
if max_possible.is_integer():
floor_max_possible -= 1
if floor_max_possible == floor_min_possible:
return floor_max_possible
mid = (min_possible + max_possible)/2
if coin_flip():
min_possible = mid
else:
max_possible = mid
Примечание: я проверил этот код по методу принятия / отклонения, и оба получили равномерное распределение. Этот код требует меньше бросков монеты, чем прием отклонения, за исключением случаев, когда b - a близко к следующей степени 2. Например, если вы хотите сгенерировать a = 0, b = 62, тогда лучше принять / отклонить. Мне удалось доказать, что этот код может использовать в среднем не более, чем на 2 броска монеты больше, чем принять / отклонить. Из моего прочтения похоже, что Кнут и Яо (1976) дали метод решения этой проблемы и доказали, что их метод является оптимальным при ожидаемом количестве подбрасываний монет. Они также доказали, что ожидаемое число переворотов должно быть больше энтропии Шеннона распределения. Однако я не смог найти копию текста статьи, и мне было бы интересно узнать, каков их метод. (Обновление: только что нашел экспозицию Кнута Яо 1976 года здесь:http://www.nrbook.com/devroye/Devroye_files/chapter_fifteen_1.pdf, но я еще не читал его). Кто-то также упомянул Хана Хоши в этой теме, которая кажется более общей, и решает ее с помощью пристрастной монеты. Смотрите также http://paper.ijcsns.org/07_book/200909/20090930.pdf Pae (2009) для хорошего обсуждения литературы.