Позвольте мне перефразировать ваш вопрос:
Пусть random()
- генератор случайных чисел с дискретным равномерным распределением по [0,1)
. Пусть D
будет числом возможных значений, возвращаемых random()
, каждое из которых точно на 1/D
больше предыдущего. Создайте генератор случайных чисел rand(L, U)
с дискретным равномерным распределением по [L, U)
, чтобы каждое возможное значение было точно на 1/D
больше предыдущего.
-
Пара быстрых заметок.
- Проблема в этой форме, и, как вы ее сформулировали, она неразрешима. Тот
если N = 1, мы ничего не можем сделать.
- Мне не требуется, чтобы
0.0
было одним из возможных значений для random()
. Если это не так, то возможно, что приведенное ниже решение потерпит неудачу, когда U - L < 1 / D
. Меня это не особо беспокоит.
- Я использую все полуоткрытые диапазоны, потому что это упрощает анализ. Использовать закрытые диапазоны было бы просто, но утомительно.
Наконец, хорошие вещи. Ключевым моментом здесь является то, что плотность можно поддерживать, независимо выбирая целые и дробные части результата.
Во-первых, обратите внимание, что с учетом random()
создать randomBit()
тривиально. То есть
randomBit() { return random() >= 0.5; }
Затем, если мы хотим выбрать один из {0, 1, 2, ..., 2^N - 1}
равномерно случайным образом, то есть просто с помощью randomBit()
, просто сгенерировать каждый из битов. Назовите это random2(N)
.
Используя random2()
, мы можем выбрать один из {0, 1, 2, ..., N - 1}
:
randomInt(N) { while ((val = random2(ceil(log2(N)))) >= N); return val; }
Теперь, если известно D
, то проблема тривиальна, поскольку мы можем сократить ее до простого простого выбора одного из floor((U - L) * D)
значений равномерно случайным образом, и мы можем сделать это с помощью randomInt()
.
Итак, давайте предположим, что D
неизвестно. Теперь давайте сначала создадим функцию для генерации случайных значений в диапазоне [0, 2^N)
с правильной плотностью. Это просто.
rand2D(N) { return random2(N) + random(); }
rand2D()
- это то место, где мы требуем, чтобы разница между последовательными возможными значениями для random()
была точно 1/D
. Если нет, то возможные значения здесь не будут иметь равномерную плотность.
Далее нам нужна функция, которая выбирает значение в диапазоне [0, V)
с правильной плотностью. Это похоже на randomInt()
выше.
randD(V) { while ((val = rand2D(ceil(log2(V)))) >= V); return val; }
И наконец ...
rand(L, U) { return L + randD(U - L); }
Теперь мы можем сместить дискретные позиции, если L / D
не является целым числом, но это неважно.
-
Последнее замечание: вы могли заметить, что некоторые из этих функций могут никогда не завершиться. Это по существу требование. Например, random()
может иметь только один бит случайности. Если я затем попрошу вас выбрать одно из трех значений, вы не сможете сделать это случайным образом с функцией, которая гарантированно завершится.