Метод обратной функции
У нас есть равномерные числа из $[0, 1)$ — и есть потребность в выборках из совсем других распределений: экспоненциального для времени между отказами, нормального для шумов измерений, какого угодно. Как перекинуть мост от ровного ковра равномерности к нужной форме? Самый изящный и универсальный способ — метод обратной функции.
Метод обратной функции (inverse transform sampling) — приём генерации случайной величины с заданным распределением: берут равномерное число $u$ из $[0, 1)$ и подставляют его в обратную функцию распределения, получая выборку нужной формы.
В основе метода лежит одно ключевое понятие — функция распределения. Напомним: CDF (cumulative distribution function), обозначаемая $F(x)$, — это вероятность того, что случайная величина окажется не больше $x$. Она всегда монотонно растёт от 0 (слева) до 1 (справа). И вот ключевое наблюдение: значения самой CDF равномерно распределены на $[0, 1)$. Значит, если «прочитать формулу наоборот» — взять равномерное $u$ и найти такой $x$, что $F(x) = u$, — мы получим величину как раз с нужным распределением.
Рецепт из трёх шагов
Метод сводится к универсальному алгоритму, который работает для любого распределения с обратимой CDF:
- Взять CDF целевого распределения $F(x)$ — формулу, описывающую накопленную вероятность.
- Приравнять её к равномерному числу: $F(x) = u$, где
uвзято изrandom.random(). - Решить уравнение относительно x: выразить $x = F^{-1}(u)$. Полученное $x$ и есть выборка из нужного распределения.
Геометрически это «отражение через диагональ»: мы переходим от вопроса «какова вероятность не превысить x?» к обратному «какому x соответствует данная накопленная вероятность u?». Поскольку u бежит равномерно по $[0, 1)$, а CDF в густонаселённых областях распределения растёт круто, туда же чаще и «приземляются» наши x — так и воспроизводится нужная плотность.
Разбор на экспоненциальном распределении
Экспоненциальное распределение — классика моделирования: им описывают время до отказа прибора, интервалы между приходами клиентов, длительность телефонного разговора. У него один параметр $\lambda$ (лямбда) — интенсивность событий. Его CDF имеет вид $F(x) = 1 - e^{-\lambda x}$ для $x \ge 0$.
Применим рецепт. Приравниваем к равномерному числу и решаем относительно x:
$$\begin{aligned} F(x) &= u \\ 1 - e^{-\lambda x} &= u \\ e^{-\lambda x} &= 1 - u \\ -\lambda x &= \ln(1 - u) \\ x &= -\ln(1 - u) / \lambda \end{aligned}$$
Готова формула генерации: $x = -\ln(1 - u) / \lambda$. Каждое равномерное u мы прогоняем через неё и получаем число, распределённое экспоненциально. Проверим это эмпирически: сгенерируем десять тысяч выборок при $\lambda = 0.5$ и сравним среднее с теоретическим. Для экспоненциального распределения математическое ожидание равно $1/\lambda$, то есть при $\lambda = 0.5$ должно получиться 2.0:
import random, math, statistics
random.seed(3)
lam = 0.5
samples = [-math.log(1 - random.random()) / lam for _ in range(10000)]
print(f"Сгенерировано выборок: {len(samples)}")
print(f"Среднее выборки: {statistics.mean(samples):.4f}")
print(f"Теоретическое 1/lambda: {1 / lam}")
Вывод:
Сгенерировано выборок: 10000 Среднее выборки: 2.0107 Теоретическое 1/lambda: 2.0
Среднее выборки — 2.0107 — почти в точности совпало с теоретическим значением 2.0. Расхождение в сотых — это нормальный статистический «шум» конечной выборки; на ещё большем числе экспериментов оно стало бы ещё меньше. Метод сработал: мы взяли равномерные числа и арифметической формулой превратили их в честную экспоненциальную выборку, ни разу не обратившись к специальной функции вроде random.expovariate.
Как работает под капотом
Почему «отражение CDF» вообще даёт правильное распределение? Здесь работает фундаментальный факт теории вероятностей. Возьмём случайную величину $X$ с непрерывной строго возрастающей CDF $F$. Тогда величина $U = F(X)$ равномерно распределена на $[0, 1)$ — этот результат называют интегральным преобразованием вероятности. Метод обратной функции просто читает его «задом наперёд»: если $U$ равномерно, то $X = F^{-1}(U)$ имеет ровно распределение с функцией $F$.
Интуиция — в скорости роста CDF. Там, где плотность распределения высокая, CDF поднимается круто: небольшому шагу по x соответствует большой шаг по вероятности. А поскольку u ложится равномерно по вертикали $[0, 1)$, на крутые (плотные) участки приходится больше попаданий, на пологие (редкие) — меньше. Так равномерность по вертикали превращается в нужную неравномерность по горизонтали.
Маленькая, но важная деталь реализации: в формуле стоит $1 - u$, а не просто $u$. Математически $1 - u$ и $u$ распределены одинаково равномерно, так что обе версии корректны. Но $1 - u$ с random.random() гарантированно лежит в полуинтервале $(0, 1]$, то есть никогда не обращается в ноль, — а $\ln(0)$ не определён. Запись через $1 - u$ страхует от логарифма нуля, если генератор вдруг вернёт ровно 0.0.
Частые ошибки
- Пытаться применить метод там, где CDF не обращается аналитически. Для нормального распределения обратной CDF в элементарных функциях не существует — приходится использовать численные приближения или совсем другой метод. Главное ограничение приёма: нужна обратимая (хотя бы численно) функция распределения.
- Подставлять плотность вместо функции распределения. В рецепте участвует именно CDF $F(x)$ (накопленная вероятность), а не плотность $f(x)$. Подстановка плотности даёт бессмысленный результат.
- Брать $\ln(u)$ и натыкаться на $\ln(0)$. Если
random.random()вернёт ровно 0.0, то $\ln(0)$ вызовет ошибку. Запись через $1 - u$ устраняет эту опасность, оставляя аргумент логарифма строго положительным. - Путать $\lambda$ и $1/\lambda$. Параметр $\lambda$ — это интенсивность (сколько событий в единицу времени), а среднее экспоненциального распределения равно $1/\lambda$. При $\lambda = 0.5$ среднее равно 2, а не 0.5 — лёгкая, но частая путаница.
- Забывать про область определения. Экспоненциальная CDF задана для $x \ge 0$; формула $-\ln(1 - u)/\lambda$ при положительном $\lambda$ всегда даёт неотрицательное число — и это правильно. Если знак или область перепутаны, можно получить отрицательные «времена», лишённые смысла.
Итоги
- Метод обратной функции превращает равномерное
uв выборку любого распределения с обратимой CDF: $x = F^{-1}(u)$. - Универсальный рецепт: взять CDF, приравнять $F(x) = u$, решить относительно $x$.
- Для экспоненциального распределения это даёт формулу $x = -\ln(1 - u) / \lambda$; среднее выборки сходится к теоретическому $1/\lambda$.
- Метод работает, потому что $F(X)$ всегда равномерно (интегральное преобразование вероятности), а крутизна CDF переносит плотность в нужные места.
- Главное ограничение — нужна обратимая CDF; для нормального распределения, например, аналитической обратной функции нет.