Какова вероятность выбросить 7 на двух кубиках? Можно посчитать на бумаге, а можно смоделировать: бросить кубики много раз и посмотреть на долю удачных бросков. Метод, который отвечает на вопросы о вероятностях повторением случайных экспериментов, называют методом Монте-Карло — в честь Монте-Карло, города казино.
Приём, который делает моделирование на NumPy быстрым: один эксперимент — одна строка таблицы. Генератор
сразу выдаёт таблицу size=(экспериментов, бросков), итог каждого эксперимента — агрегат по axis=1, а
вероятность — среднее маски по всем строкам. Ни одного цикла.
Два кубика
import numpy as np
rng = np.random.default_rng(1)
throws = rng.integers(1, 7, size=(100000, 2)) # строка — один бросок двух кубиков
print(throws[:3])
sums = throws.sum(axis=1)
print((sums == 7).mean())[[3 4] [5 6] [1 1]] 0.16619
Точный ответ — 6 из 36 = 0.1667: из 36 равновероятных пар ровно 6 дают семь. Моделирование попало почти в точку.
Создайте генератор с seed 12 и бросьте два кубика 100 000 раз (таблица 100 000 × 2). Сохраните в doubles
долю бросков, где на кубиках выпало одно и то же число.
rng.integers(1, 7, size=(100000, 2)).pairs[:, 0] == pairs[:, 1]; доля — среднее маски.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
rng = ...
doubles = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
rng = np.random.default_rng(12)
pairs = rng.integers(1, 7, size=(100000, 2))
doubles = (pairs[:, 0] == pairs[:, 1]).mean()Какова точная вероятность дубля на двух кубиках?
Сколько орлов
Создайте генератор с seed 5 и смоделируйте 50 000 серий по 5 бросков монеты (таблица 50 000 × 5 из маски
< 0.5). Сохраните в at_least_3 долю серий, где орлов было не меньше трёх.
rng.random((50000, 5)) < 0.5; True — орёл.axis=1; дальше — доля серий, где она не меньше 3.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
rng = ...
at_least_3 = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
rng = np.random.default_rng(5)
flips = rng.random((50000, 5)) < 0.5
at_least_3 = (flips.sum(axis=1) >= 3).mean()Число π
Бросим точки в квадрат со стороной 1. Четверть круга радиусом 1 занимает π/4 площади квадрата — значит, доля точек, попавших в круг (x² + y² ≤ 1), примерно равна π/4:
rng = np.random.default_rng(1)
points = rng.random((100000, 2))
inside = (points[:, 0] ** 2 + points[:, 1] ** 2) <= 1
print(inside.mean() * 4)3.145
Создайте генератор с seed 3, бросьте 100 000 точек (таблица 100 000 × 2 из rng.random) и сохраните в
pi_est оценку числа π: доля точек внутри четверти круга, умноженная на 4.
rng.random((100000, 2)), столбец 0 — x, столбец 1 — y.x**2 + y**2 <= 1; доля — среднее маски.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
rng = ...
pi_est = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
rng = np.random.default_rng(3)
pts = rng.random((100000, 2))
pi_est = ((pts[:, 0] ** 2 + pts[:, 1] ** 2) <= 1).mean() * 4Парадокс дней рождения
Сколько человек нужно собрать, чтобы у двоих совпал день рождения с вероятностью около 50 %? Интуиция говорит «человек 180», а ответ — 23. Проверим: строка — группа из 23 человек, дни — случайные числа от 1 до 365. Совпадение в группе легко найти, если отсортировать строку: тогда одинаковые дни окажутся соседями.
group = np.sort(np.random.default_rng(6).integers(1, 366, size=23))
print(group)
print(group[1:] == group[:-1])
print((group[1:] == group[:-1]).any())[ 19 45 69 121 126 135 137 156 163 165 165 189 197 226 231 241 246 247 249 276 324 346 361] [False False False False False False False False False True False False False False False False False False False False False False] True
group[1:] == group[:-1] сравнивает каждый элемент с предыдущим — два среза одной строки, сдвинутые на один.
В этой группе два человека родились в 165-й день года — рядом после сортировки, и в маске появился True.
Создайте генератор с seed 23, смоделируйте 10 000 групп по 23 человека и сохраните в same_bday долю групп,
где хотя бы у двоих совпал день рождения.
rng.integers(1, 366, size=(10000, 23)), отсортированные по строкам: np.sort(…, axis=1) сортирует каждую строку отдельно.days[:, 1:] == days[:, :-1]; «есть совпадение» — .any(axis=1).Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
rng = ...
same_bday = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
rng = np.random.default_rng(23)
days = np.sort(rng.integers(1, 366, size=(10000, 23)), axis=1)
same_bday = (days[:, 1:] == days[:, :-1]).any(axis=1).mean()rng = np.random.default_rng(0)
big = rng.integers(1, 7, size=(1000000, 2)).sum(axis=1)
print(len(big), (big == 7).mean())1000000 0.166822
Итоги
- Метод Монте-Карло: вероятность ≈ доля удачных исходов в большом числе случайных экспериментов.
- Эксперимент — строка таблицы
size=(n, k); итог эксперимента — агрегат поaxis=1. .any(axis=1),.sum(axis=1)и сравнения столбцов отвечают на «было ли» и «сколько» в каждой строке.- Чем больше экспериментов, тем точнее оценка.