Но уж больно средние числа она возвращает из отрезка.
О, вы переоткрыли биномиальное распределение.
А вот алгоритм, точно дающий равномерное распределение: накапливаем биты, пока не накопится альтернатив не меньше чем нам надо, после чего выдаём результат, если он среди первых
альтернатив, а если нет, вычитаем
альтернатив и продолжаем удваивать их число, пока опять их не станет достаточно, и т. д. пока нам наконец не повезёт. После этого мы можем сохранить оставшиеся альтернативы, чтобы сэкономить энтропию, выданную нам генератором, а можем и не экономить и начать с одной альтернативы. Алгоритм придумал не я, но я его жутко люблю.
Пример, потому что лень было сделать описание вменяемым: допустим, нам надо сгенерировать 0..4. Будем считать удачными лексикографически первые альтернативы из полного списка возможных. Пошли генерировать:
ε: одна альтернатива, мало.
ε —1→ 1: две 0, 1, мало.
1 —0→ 10: четыре 00, 01, 10, 11, мало.
10 —1→ 101: восемь 000..111 — но получившееся у нас 101 не попадает в 000..100. Эти пять альтернатив сгорают. Теперь у нас три альтернативы 101, 110, 111.
101 —1→ 1011: шесть 1010, 1011, 1100, 1101, 1110, 1111, мы попали наконец в первые пять, так что выдаём 1 (1010 соответствует 0, …, 1110 соответствует 4).
(Теперь у нас осталась одна альтернатива 1111 на будущее, и в этом случае нет разницы между экономящим энтропию и экономящим память алгоритмами.)
Оно конечно кодируется много проще, чем описано тут. Неподходящие альтернативы всегда соответствуют какому-то концу списка двоичных строк, упорядоченных лексикографически, так что мы можем накапливать биты в целом числе и потом когда полное количество альтернатив выходит больше
, если в числе меньше
, выдавать его (плюс
), а если больше, вычитать из него
так же как и из количества альтернатив и продолжать попытки.
-- Вс сен 27, 2020 03:00:29 --Другое описание от
Null:
https://dxdy.ru/post947484.html#p947484.
-- Вс сен 27, 2020 03:03:21 --Другим вариантом того же алгоритма будет представить число
как дробь (собственно
выше это и делает) и накапливать биты дробной части, столько чтобы при умножении
младший значащий бит оставался ещё в дробной части. И тоже проверить достижение правой границы.
А, ну это же вроде то же самое? Но зато в формулировке выше не надо проверять границы.