🎲🐍 Моделируем игру в кости на Python с помощью метода Монте-Карло

В этой статье учимся использовать метод Монте-Карло для прогнозирования вероятностей.

Что такое моделирование методом Монте-Карло?

Моделирование Монте-Карло – это тип вычислительного алгоритма, оценивающий вероятность возникновения неопределенного события из-за участия случайных величин. Алгоритм основан на повторной случайной выборке в попытке определить вероятность. Это означает моделирование событий со случайными входными данными большое число раз для получения оценки. Также определяются и другие факторы, которые будут видны на примере. Моделирование по методу Монте-Карло используется в экономике, азартных играх, машиностроении, энергетической отрасли. Поэтому знание этого метода будет полезно для многих областей. Модель Монте-Карло легко понять на примере игры в кости.

Игра в кости

В этой игре будут задействованы 2 шестигранных кубика. Для выигрыша игроку требуется выбросить одинаковое число на обоих кубиках. Шестигранный кубик имеет 6 возможных исходов (1, 2, 3, 4, 5 и 6). С двумя кубиками 36 возможных исходов (1 и 1, 1 и 2, 1 и 3 и т. д., или 6 х 6 = 36 вариантов). В этой игре у заведения больше шансов на победу (30 исходов против 6 у игроков), следовательно, у заведения значительное преимущество.

Предположим, игрок начинает с баланса в 1000$ и готов проиграть все, поэтому ставит 1$ на каждый бросок (оба кубика) и решает сделать 1000 бросков. Предположим, что заведение щедрое и выплата будет в 4 раза больше ставки, когда игрок выигрывает. Например, если игрок выиграет первый бросок, баланс увеличится на 4$, и раунд закончится с 1004$. Если чудом получится выиграть серию из 1000 бросков, то итого получится 5000$. В случае каждого неудачного раунда получится 0$.

Импорт библиотек Python

Смоделируем игру, чтобы выяснить, сделал ли игрок правильный выбор. Начнем код с импорта необходимых библиотек Python: Pyplot из Matplotlib и random. Для визуализации результатов будет использоваться библиотека Pyplot, для моделирования броска шестигранной игральной кости – random.

# Импорт библиотек
import matplotlib.pyplot as plt
import random

Функция броска кубиков

Далее определим функцию, которая будет выдавать случайное целое число от 1 до 6 для обеих костей (имитация броска). Функция будет сравнивать значение обеих костей и возвращать логическое значение – одинаковы броски или нет. Позже это значение будет использоваться для определения действий в коде.

# Создаем функцию броска кубика
def roll_dice():
    die_1 = random.randint(1, 6)
    die_2 = random.randint(1, 6)

    # Определим является ли значение на костях одинаковым
    if die_1 == die_2:
        same_num = True
    else:
        same_num = False
    return same_num

Входные данные и отслеживаемые переменные

Каждое моделирование методом Монте-Карло требует знание входных данных и информации, которую требуется получить. Входные данные определены, когда описывалась игра. Количество бросков за игру 1000 раз, сумма за бросок 1$. В дополнение требуется определить – сколько раз игра будет моделироваться. В качестве счетчика Монте-Карло используем переменную num_simulations. Чем больше это число, тем точнее прогнозируемая вероятность соответствует истинному значению.

Число переменных, которые возможно отслеживать, растет с ростом сложности проекта, поэтому требуется определить, какая информация нужна. В данном примере будет отслеживаться вероятность выигрыша (выигрыш за игру, деленный на общее число бросков) и конечный баланс для каждой симуляции (игры). Эти переменные будут инициализироваться, как списки и будут обновляться в конце каждой игры.

# Входные данные
num_simulations = 10000
max_num_rolls = 1000
bet = 1

# Отслеживаемые переменные
win_probability = []
end_balance = []

Настройка фигуры

Следующий шаг – это настройка фигуры перед запуском симуляции: это позволит добавлять линии к рисунку после каждой игры. После запуска всех симуляций отобразим график, чтобы показать результаты.

# Создание фигуры, для симуляции баланса
fig = plt.figure()
plt.title("Monte Carlo Dice Game [" + str(num_simulations) + "   
          simulations]")
plt.xlabel("Roll Number")
plt.ylabel("Balance [$]")
plt.xlim([0, max_num_rolls])

Моделирование Монте-Карло

В коде ниже 2 цикла: внешний, который перебирает заданное кол-во симуляций (10000) и вложенный, который запускает каждую игру (1000 бросков). Перед запуском каждого цикла while инициализируется:

  • баланс игрока как 1000$(в виде списка для построения графиков);
  • количество бросков и выигрышей.

Цикл while будет моделировать игру на 1000 бросков. Внутри этого цикла бросаются кости, используя логическую переменную, возвращаемую функцией roll_dice() для определения результата. Если кости одинаковые – в список баланса добавляется 4-кратная ставка и выигрыш к счету игрока. Если кости разные – вычитается ставка из списка баланса. В конце каждого броска добавляем счетчик в список num_rolls.

Как только количество бросков достигло 1000, рассчитываем вероятность выигрыша игрока, как количество выигрышей, деленное на общее количество бросков. Сохраняем конечный баланс завершенной игры в отслеживаемой переменной end_balance.

# Цикл for запускает желаемое количество симуляций
for i in range(num_simulations):
    balance = [1000]
    num_rolls = [0]
    num_wins = 0
    # Выполняется до тех пор пока игрок не выкинет 1000 раз
    while num_rolls[-1] < max_num_rolls:
        same = roll_dice()
        # Результат если кости одинаковые
        if same:
            balance.append(balance[-1] + 4 * bet)
            num_wins += 1
        # Результат если кости разные
        else:
            balance.append(balance[-1] - bet)

        num_rolls.append(num_rolls[-1] + 1)
    # Сохраняем отслеживаемую переменную и добавляем строку к рисунку
    win_probability.append(num_wins/num_rolls[-1])
    end_balance.append(balance[-1])
    plt.plot(num_rolls, balance)

Получение результатов

Последний шаг – вывод осмысленных данных из отслеживаемых переменных. Отобразим фигуру (показанную ниже), созданную в цикле for. Также рассчитаем и отобразим общую вероятность выигрыша и конечный баланс, усредняя списки win_probability и end_balance.

# Выведем график после завершения моделирования
plt.show()

# Усредненная вероятность выигрыша и конечного баланса
overall_win_probability = sum(win_probability)/len(win_probability)
overall_end_balance = sum(end_balance)/len(end_balance)
# Вывод средних значений
print("Average win probability after " + str(num_simulations) + "   
       runs: " + str(overall_win_probability))
print("Average ending balance after " + str(num_simulations) + " 
       runs: $" + str(overall_end_balance))
Моделирование игры в кости:

Average win probability after 10000runs: 0.16666139999999954
Average ending balance after 10000runs: $833.307

Анализ результатов

Сделаем вывод из результатов. Из рисунка выше возможно определить, что игрок редко получает прибыль после 1000 бросков. Средний конечный баланс 10000 симуляций составляет 833,66$ (из-за рандомизации, возможны различия результатов). Казино остается в выигрыше даже, если выплачивает в четыре раза больше при победе игрока.

Также заметим, что вероятность выигрыша составляет 0.1667 или 1/6. Выше было отмечено, что у игрока 6 выигрышных исходов из 36 возможных. Используя эти 2 числа ожидаемо, что игрок выиграет 6 из 36 бросков, или 1/6 бросков, что соответствует прогнозу Монте-Карло.

***

Материалы по теме

Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека питониста»

Источники

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ

admin
11 декабря 2018

ООП на Python: концепции, принципы и примеры реализации

Программирование на Python допускает различные методологии, но в его основе...
admin
14 июля 2017

Пишем свою нейросеть: пошаговое руководство

Отличный гайд про нейросеть от теории к практике. Вы узнаете из каких элеме...