Ответ 1
Иногда это помогает начать с легко понятного примера, а затем обобщить оттуда. Чтобы все было просто, предположим, что arc4random
возвращает uint8_t
вместо uint32_t
, поэтому вывод из arc4random
является числом в интервале [0,256)
. И пусть выбрать upper_bound
из 7.
Заметим, что 7 равномерно не делит на 256
256 = 7 * 36 + 4
Это означает, что наивное использование операции modulo для получения псевдослучайных чисел, меньших 7, приведет к следующему распределению вероятности
37/256 for outcomes 0,1,2,3
36/256 for outcomes 4,5,6
То, что известно как смещение по модулю, результаты 0,1,2,3 более вероятны, чем результаты 4,5,6.
Чтобы избежать смещения по модулю, мы могли просто отклонить значения 252, 253, 254, 255 и сгенерировать новое число, пока результат не окажется в интервале [0,252)
. Все числа в интервале [0,252)
имеют равную вероятность (отклонение более высоких чисел не влияет на распределение младших чисел). И так как 7 равномерно делит на 252, результирующее распределение вероятности равномерно
36/252 for outcomes 0,1,2,3,4,5,6,7
По сути, что делает arc4random_uniform
, за исключением того, что arc4random_uniform
отклоняет числа в нижней части диапазона. В частности, интервал A будет
[2^8 % 7, 2^8) which is [4, 256)
После генерации числа (назовем его N
) в интервале [4,256] окончательный расчет
outcome = N % 7
В интервале [4,256] имеется 252 числа, а так как 252 кратно 7, каждый результат на интервале [0,7] имеет равную вероятность.
Как работает arc4random_uniform, он отклоняет/повторяет на небольшом диапазоне чисел, а количество чисел в оставшемся диапазоне кратно верхнему. (Так как upper_bound обычно является небольшим числом по сравнению с 2 ^ 32, вероятность наличия нескольких попыток для одного результата довольно мала).
Но вы действительно заботитесь о модульной предвзятости? В большинстве случаев ответ: "Нет". Рассмотрим наш пример с верхней оценкой 7. Распределение вероятности для наивного по модулю реализации
613566757 / 4294967296 for outcomes 0,1,2,3
613566756 / 4294967296 for outcomes 4,5,6
который является модульным смещением менее 0,0000002%.
Итак, ваш выбор: либо потратьте небольшое количество времени на повторные попытки, чтобы получить идеальное распределение, либо принять незначительную ошибку в распределении вероятности, чтобы избежать повторений.