Одна из характеристик хорошей rand-функции — равномерное распределение. Это означает, что каждое возможное значение имеет одинаковую вероятность быть выбрано
Представим, что у вас есть функция
rand7(), которая равномерно возвращает случайное число в диапазоне [1, 7]. Наша задача — написать функцию rand10() с диапазоном [1, 10], сохранив равномерное распределение. Дальше вместо rand7() будут использоваться переменные a и b. Скорее всего, вы сходу предложите одну из примитивных реализаций. Вот примеры:func rand10() int {
a, b := rand7(), rand7()
return a + b % 3
}func rand10() int {
a, b := rand7(), rand7()
return (a * b) % 10 + 1
}Обе реализации неправильны
В первом варианте
b % 3 даёт остатки от деления на 3, т.е. 0, 1 или 2 не с одинаковой вероятностью. Остаток 1 встречается чаще остальных, из-за чего получается неравномерное распределение итоговых значений.Во втором варианте количество способов получить каждое произведение
a * b тоже неравномерное.Например:
-
1 = 1 * 1 → 1 комбинация-
6 = 1*6, 2*3, 3*2, 6*1 → 4 комбинации-
12 = 2*6, 3*4, 4*3, 6*2 → 4 комбинации-
36 = 6*6 → 1 комбинацияи так далее
(...) % 10 + 1 ещё больше усугубляет ситуацию, потому что например при product = 2, 12, 22, 32, 42 мы будем везде получать значение 3На графиках можно наглядно увидеть перекос, перебрав все 49 возможных комбинаций
a и b.Правильное решение
Рассмотрим формулу:
((a - 1) * 7 + b ) % 10 + 1Она почти равномерна: значения от
1 до 10 встречаются почти с одинаковой частотой, за исключением 10, которое появляется всего 4 раза, тогда как остальные — по 5 раз.Причина проста - всего комбинаций
7 × 7 = 49, нам не хватает числа 50. Можно ли без него? Можно - проблема решается с помощью rejection sampling - мы отбрасываем значения больше 40. На языке кода мы делем пересчёт
if num > 40. Тогда остаются только 40 комбинаций, которые можно равномерно разбить на 10 групп по 4 значения, что и видно на последней диаграммеФинальный код:
func rand10() int {
for {
a, b := rand7(), rand7()
num := (a-1)*7 + b
if num <= 40 {
return 1 + (num-1)%10
}
}
}Задача на Leetcode: https://leetcode.com/problems/implement-rand10-using-rand7/
