Симуляция вероятностей и закон больших чисел
Когда формула сложна или её лень выводить, вероятность можно просто «измерить» — много раз повторив эксперимент в коде.
Метод Монте-Карло — оценка вероятности через многократное случайное моделирование: доля «успехов» в большом числе испытаний приближает истинную вероятность.
Закон больших чисел
Главная идея, делающая симуляцию законной: чем больше испытаний, тем ближе наблюдаемая доля успехов к истинной вероятности. Подбросим монету и посмотрим, как доля орлов «успокаивается» к 0.5 по мере роста числа бросков.
import random
random.seed(1)
def heads_fraction(n):
heads = sum(random.random() < 0.5 for _ in range(n))
return heads / n
for n in [10, 100, 1000, 10000, 100000]:
print(f"{n:6} бросков -> доля орлов: {heads_fraction(n):.4f}")
Вывод:
10 бросков -> доля орлов: 0.6000 100 бросков -> доля орлов: 0.4600 1000 бросков -> доля орлов: 0.4800 10000 бросков -> доля орлов: 0.5037 100000 бросков -> доля орлов: 0.5003
На 10 бросках доля скачет (0.60 — далеко от 0.5), но к 100000 она прилипает к 0.5. Это закон больших чисел в действии. Отсюда мораль для аналитика: на маленькой выборке случайность велика, делать выводы по 10 наблюдениям опасно.
Оцениваем вероятность перебором
Теперь решим задачу, которую считать формулой муторно: «при броске двух костей сумма очков равна 7». Просто смоделируем десятки тысяч бросков.
import random
random.seed(42)
N = 200000
count = 0
for _ in range(N):
a = random.randint(1, 6)
b = random.randint(1, 6)
if a + b == 7:
count += 1
print("Оценка P(сумма = 7):", round(count / N, 4))
print("Точное значение 6/36:", round(6 / 36, 4))
Вывод:
Оценка P(сумма = 7): 0.165 Точное значение 6/36: 0.1667
Симуляция дала 0.165 против точных 0.1667 — отличное совпадение. Сумма 7 — самая вероятная при двух костях, потому что её даёт больше всего комбинаций: (1,6), (2,5), (3,4), (4,3), (5,2), (6,1).
Симуляция там, где формулы трудны
Сила Монте-Карло — в задачах, где аналитическое решение громоздко. Например: «сколько в среднем бросков кубика нужно, чтобы выпала шестёрка?» Можно вывести формулу, а можно — измерить.
import random
random.seed(10)
def rolls_until_six():
count = 0
while True:
count += 1
if random.randint(1, 6) == 6:
return count
N = 100000
total = sum(rolls_until_six() for _ in range(N))
print("Среднее число бросков до шестёрки:", round(total / N, 3))
print("Теория (ожидание = 6):", 6)
Вывод:
Среднее число бросков до шестёрки: 6.008 Теория (ожидание = 6): 6
В среднем нужно ровно 6 бросков — и симуляция это подтвердила, хотя мы не выводили ни одной формулы. Это и есть главная ценность метода: сложную вероятностную задачу можно «прощупать» кодом.
Когда симуляция уместна
- формула слишком сложна или её вывод занимает много времени;
- нужно быстро проверить аналитический ответ («а не ошибся ли я?»);
- система имеет много взаимодействующих случайных частей.
Главное — делать достаточно испытаний (тысячи и больше) и при необходимости фиксировать random.seed, чтобы результат был воспроизводим.
Итог
- Метод Монте-Карло оценивает вероятность как долю успехов в большом числе случайных испытаний.
- Закон больших чисел: с ростом числа испытаний доля успехов сходится к истинной вероятности.
- На малых выборках случайность велика — не доверяйте выводам по нескольким наблюдениям.
random.seedделает симуляцию воспроизводимой.