Симуляция вероятностей и закон больших чисел

Когда формула сложна или её лень выводить, вероятность можно просто «измерить» — много раз повторив эксперимент в коде.

Метод Монте-Карло — оценка вероятности через многократное случайное моделирование: доля «успехов» в большом числе испытаний приближает истинную вероятность.

Закон больших чисел

Главная идея, делающая симуляцию законной: чем больше испытаний, тем ближе наблюдаемая доля успехов к истинной вероятности. Подбросим монету и посмотрим, как доля орлов «успокаивается» к 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 делает симуляцию воспроизводимой.
Проверьте себя
1. Что утверждает закон больших чисел?
AЧем больше чисел, тем они больше
BС ростом числа испытаний доля успехов приближается к истинной вероятности
CБольшие выборки всегда дают ровно теоретический результат
DВероятность растёт с числом испытаний
2. В чём суть метода Монте-Карло?
AВ выводе точной формулы вероятности
BВ оценке вероятности через многократное случайное моделирование
CВ использовании только маленьких выборок
DВ округлении результатов до целых
3. Зачем в симуляциях фиксируют random.seed?
AЧтобы ускорить вычисления
BЧтобы результат был воспроизводимым при повторных запусках
CЧтобы увеличить точность вероятности
DЭто обязательное требование Python
Поддержать проект