Моделируем игру в рулетку на Python
easy

Моделируем игру в рулетку на Python

И строим наглядные графики.

Недавно у нас была статья о математическом ожидании в рулетке. Сегодня мы смоделируем игру на рулетке и покажем силу математики на практике. 

👉 Вкратце: что нужно знать. В рулетку можно выиграть в краткосрочной перспективе, но когда играешь долго, ты всегда теряешь деньги. Почему так: 

  • У рулетки 37 секторов: 18 красных, 18 чёрных и зеро. Самая простая ставка — на красное или на чёрное. Если ставка сработала, то она удваивается. Если не сработала, то ставка сгорает.
  • По идее, в половине случаев ставка должна принести прибыль, поэтому играть в эту игру должно быть выгодно. Поставил большую сумму, удвоил, забрал. На первый взгляд, шанс — 50%.
  • Но на самом деле красное или чёрное выпадают не в половине случаев, а чуть реже — за счёт зеро. Из-за него вероятность красного или чёрного не 50%, а 48,6%.
  • Получается, при идеально честных рулеточных условиях проигрывать мы будем всегда немного чаще, чем выигрывать. И в долгосрочной перспективе мы будем всегда немного терять деньги. 

Но так ли это на самом деле? Может быть, здесь есть какой-то математический трюк, из-за которого мы видим искажённые результаты? Давайте построим модель и протестируем всё. 

Что проверяем

Мы смоделируем на Python игру в рулетку по трём стратегиям с разными матожиданиями и посмотрим, как они влияют на результат. Чтобы было нагляднее, мы построим график ставок и выигрышей для каждой стратегии. Python мы выберем именно для того, чтобы быстро построить график. 

В каждой стратегии мы всегда начинаем с миллиона рублей, а ставим одну тысячную от этой суммы. Если захотите, можно будет потом поменять это в коде. 

Мы сделаем три модели рулетки:

  1. Классика. В первом случае мы смоделируем классическую рулетку как в казино с отрицательным матожиданием. Для этого нам понадобится 18 чёрных цифр, 18 красных и зеро, а ставить будем только, например, на чёрное. Теоретически, играя по этой стратегии, мы должны со временем проиграть.
  2. Во второй стратегии мы сделаем нулевое матожидание — уберём зеро. Это даст нам равные шансы при каждой ставке, поэтому, скорее всего, при том же количестве ставок, что и в первый раз, мы останемся с плюс-минус той же суммой. Не ровно той же, а примерно такой же. 
  3. Третья стратегия — делаем положительное матожидание. Для этого мы убираем зеро и одну красную цифру, чтобы у нас было 18 чёрных и 17 красных. Это значит, что мы вероятнее выиграем, чем проиграем, поэтому при том же количестве ставок мы должны неплохо заработать.

Но это в теории. Как получится на практике — сейчас проверим.

⚠️ Это про Python

Мы делаем проект на Python, для его запуска нужна пайтоновская среда и программы. Мы об этом уже писали, прочитайте: Как установить Python и начать на нём писать.

Графики

Чтобы построить графики, нам понадобятся две библиотеки — Plotly и Pandas. 

Plotly отвечает за графическую часть — рисует красивые графики в браузере с помощью JavaScript. Их можно увеличивать, выделять фрагмент для анализа, скачивать себе как картинки и сравнивать данные между собой. Когда нужно нарисовать какой-то график, Plotly запускает браузер и создаёт локальную страницу с интерактивными данными.

Чтобы вся эта магия с данными работала и графики обрабатывались правильно, для Plotly нужна библиотека Pandas. Она отвечает за математику, анализ и обработку всех чисел на графике.

Устанавливаются библиотеки так:

pip install plotly
pip install pandas

Эти команды можно запустить прямо в терминале VS Code, если вы пишете код в нём, а если нет — то в командной строке компьютера.

Подготовка

Всё, что понадобится для проверки каждой стратегии, мы вынесем в отдельные переменные. Это позволит нам легко поменять нужные числа при необходимости в одном месте, а не искать по всей программе.

Для подсчёта статистики сделаем переменные, где будем хранить данные о количестве побед и проигрышей. А для графиков — будем запоминать номер игры и количество денег, которые у нас остались.

Напишем это на Python:

# подключаем модуль случайных чисел
import random

# подключаем модуль для графиков
import plotly
import plotly.graph_objs as go

# сколько денег будет на старте для каждой стратегии
startmoney = 1000000

# коэффициент ставки
c1 = 0.001

# количество побед и проигрышей
win = 0
loose = 0

# количество игр, сыгранных по первой стратегии
games = 0

# статистика для первой стратегии
balance1 = []
games1 = []

# статистика для второй стратегии
balance2 = []
games2 = []

# статистика для третьей стратегии
balance3 = []
games3 = []

Три стратегии с матожиданиями

В каждой стратегии будем ставить на цвет — красное или чёрное. Чтобы понять, сыграла ставка или нет, мы каждый раз будем брать случайное число от 1 до 37: 18 чёрных, 18 красных и зеро. Чтобы было проще, пусть числа от 1 до 18 у нас будут чёрные, с 19 по 36  — красные, а 37 будет зеро. 

Первая стратегия: берём всё как есть и просто бросаем шарик. Если выпало чёрное — мы выиграли. В этой стратегии у нас 18 выигрышных вариантов и 19 проигрышных, поэтому матожидание будет отрицательным.

Вторая стратегия: убираем зеро. У нас получается по 18 чёрных и красных чисел, и вероятность выиграть — ровно 50 на 50. Это даёт нам нулевое матожидание.

Третья: убираем зеро и одно красное число, чтобы было 18 чёрных и 17 красных. Так как вероятность попасть на чёрное выше, чем на красное, то матожидание будет положительным.

👉 Когда будете читать код, обратите внимание на такую строку: if ball in range(1,19). Дело в том, что Python работает с диапазоном range() так: первое число входит в рабочий диапазон, а конечное — нет. Поэтому, чтобы нам проверить, попало ли наше число в диапазон от 1 до 18 включительно, нам нужно указать range(1,19).

# начинаем играть с полной суммой
# первая стратегия — отрицательное матожидание, как в казино
money = startmoney
# пока у нас ещё есть деньги
while money > 0:
    # ставка —  постоянная часть от первоначальной суммы
    bet = startmoney * c1
    # если ставка получилась больше, чем у нас осталось денег — ставим всё, что осталось, чтобы не уйти в минус
    if bet > money:
        bet = money
    # после ставки количество денег уменьшилось
    money -= bet
    
    # записываем очередную игру в статистику — деньги и номер игры 
    balance1.append(money)
    games1.append(len(games1)+1)

    # крутим рулетку, на которой 18 чёрных чисел, 18 красных и одно зеро. Мы ставим на чёрное
    ball = random.randint(1,37)
    # пусть первые 18 будут чёрными — для простоты алгоритма
    # если наша ставка сыграла — мы попали в нужный диапазон
    if ball in range (1,19):
        # получаем назад нашу ставку в двойном размере
        money += bet * 2
        # увеличиваем количество побед
        win += 1
    else:
        # иначе — увеличиваем количество проигрышей
        loose += 1
games = win + loose
# выводим результат игры по первой стратегии
print("Выиграно ставок: " + str(win) + " (" + str(win/games * 100) + "%). " + " Проиграно ставок: " + str(loose)  + " (" + str(loose/games * 100) + "%). ")

# началась вторая стратегия, тоже стартуем с полной суммой
# вторая стратегия — с нулевым матожиданием
money = startmoney
# обнуляем статистику
win = 0
loose = 0

# начинаем играть с полной суммой
money = startmoney
# играем, пока есть деньги или пока мы не сыграем столько же игр, как и в первый раз
while (money > 0) and (win + loose < games):
    # ставка —  постоянная часть от первоначальной суммы
    bet = startmoney * c1
    # если ставка получилась больше, чем у нас осталось денег — ставим всё, что осталось, чтобы не уйти в минус
    if bet > money:
        bet = money
    # после ставки количество денег уменьшилось
    money -= bet
    
    # записываем очередную игру в статистику — деньги и номер игры 
    balance2.append(money)
    games2.append(len(games2)+1)

    # крутим рулетку, на которой 18 чёрных чисел, 18 красных. Так как всего поровну, матожидание будет равно нулю.
    # Ставим, как и в прошлом случае, на чёрное
    ball = random.randint(1,36)
    # пусть первые 18 будут чёрными — для простоты алгоритма
    # если наша ставка сыграла — мы попали в нужный диапазон
    if ball in range (1,19):
        # получаем назад нашу ставку в двойном размере
        money += bet * 2
        # увеличиваем количество побед
        win += 1
    else:
        # иначе — увеличиваем количество проигрышей
        loose += 1

# выводим результат игры по второй  стратегии
print("Выиграно ставок: " + str(win) + " (" + str(win/games * 100) + "%). " + " Проиграно ставок: " + str(loose)  + " (" + str(loose/games * 100) + "%). ")



# началась третья стратегия, тоже стартуем с полной суммой
# третья стратегия — с положительным матожиданием
money = startmoney
# обнуляем статистику
win = 0
loose = 0

# начинаем играть с полной суммой
money = startmoney
# играем, пока есть деньги или пока мы не сыграем столько же игр, как и в первый раз
while (money > 0) and (win + loose < games):
    # ставка —  постоянная часть от первоначальной суммы
    bet = startmoney * c1
    # если ставка получилась больше, чем у нас осталось денег — ставим всё, что осталось, чтобы не уйти в минус
    if bet > money:
        bet = money
    # после ставки количество денег уменьшилось
    money -= bet
    
    # записываем очередную игру в статистику — деньги и номер игры 
    balance3.append(money)
    games3.append(len(games3)+1)

    # крутим рулетку, на которой 18 чёрных чисел, 17 красных. Так как чёрных больше, а мы ставим на чёрное, то матожидание будет положительным
    # Ставим, как и в прошлом случае, на чёрное
    ball = random.randint(1,35)
    # пусть первые 18 будут чёрными — для простоты алгоритма
    # если наша ставка сыграла — мы попали в нужный диапазон
    if ball in range (1,19):
        # получаем назад нашу ставку в двойном размере
        money += bet * 2
        # увеличиваем количество побед
        win += 1
    else:
        # иначе — увеличиваем количество проигрышей
        loose += 1

# выводим результат игры по третьей стратегии
print("Выиграно ставок: " + str(win) + " (" + str(win/games * 100) + "%). " + " Проиграно ставок: " + str(loose)  + " (" + str(loose/games * 100) + "%). ")

Если мы запустим этот код, то в консоли увидим результат для каждого матожидания:

Моделируем игру в рулетку на Python: если мы запустим этот код, то в консоли увидим результат для каждого матожидания

Кажется, что все три варианта дают примерно одинаковый результат, но на самом деле в первом случае вы останетесь без денег, а в третьем — разбогатеете. Чтобы было нагляднее — построим графики.

Строим графики

Для графиков мы используем библиотеку Plotly и рисуем сразу все три графика на одном поле — это даст нам наглядность и понимание, к чему приводит разное матожидание. Чтобы было понятно, какой график отвечает за какую стратегию, мы их подпишем:

# строим графики
fig = go.Figure()
# для первой стратегии
fig.add_trace(go.Scatter(x=games1, y=balance1, name = "Отрицательное матожидание"))
# для второй
fig.add_trace(go.Scatter(x=games2, y=balance2, name = "Нулевое матожидание"))
# и для третьей
fig.add_trace(go.Scatter(x=games3, y=balance3, name = "Положительное матожидание"))
# выводим графики в браузер
fig.show()

Теперь посмотрим на тот же результат, который у нас получился, но в виде графиков:

Моделируем игру в рулетку на Python: теперь посмотрим на тот же результат, который у нас получился, но в виде графиков

Синяя линия — это баланс нашей первой игры с отрицательным матожиданием. Мы предсказуемо всё проиграли.

Оранжевая линия — график баланса при нулевом матожидании. Видно, что мы то выигрывали, то проигрывали, но в результате мы всё равно остались примерно с той же суммой, что и начинали.

Зелёная линия — наш баланс при положительном матожидании. Мы выигрывали чаще, чем проигрывали, поэтому удвоили нашу стартовую сумму.

# подключаем модуль случайных чисел
import random

# подключаем модуль для графиков
import plotly
import plotly.graph_objs as go

# сколько денег будет на старте для каждой стратегии
startmoney = 1000000

# коэффициент ставки
c1 = 0.001

# количество побед и проигрышей
win = 0
loose = 0

# количество игр, сыгранный по первой стратегии
games = 0

# статистика для первой стратегии
balance1 = []
games1 = []

# статистика для второй стратегии
balance2 = []
games2 = []

# статистика для третьей стратегии
balance3 = []
games3 = []

# начинаем играть с полной суммой
# первая стратегия — отрицательное матожидание, как в казино
money = startmoney
# пока у нас ещё есть деньги
while money > 0:
    # ставка —  постоянная часть от первоначальной суммы
    bet = startmoney * c1
    # если ставка получилась больше, чем у нас осталось денег — ставим всё, что осталось, чтобы не уйти в минус
    if bet > money:
        bet = money
    # после ставки количество денег уменьшилось
    money -= bet
    
    # записываем очередную игру в статистику — деньги и номер игры 
    balance1.append(money)
    games1.append(len(games1)+1)

    # крутим рулетку, на которой 18 чёрных чисел, 18 красных и одно зеро. Мы ставим на чёрное
    ball = random.randint(1,37)
    # пусть первые 18 будут чёрными — для простоты алгоритма
    # если наша ставка сыграла — мы попали в нужный диапазон
    if ball in range (1,19):
        # получаем назад нашу ставку в двойном размере
        money += bet * 2
        # увеличиваем количество побед
        win += 1
    else:
        # иначе — увеличиваем количество проигрышей
        loose += 1
games = win + loose
# выводим результат игры по первой стратегии
print("Выиграно ставок: " + str(win) + " (" + str(win/games * 100) + "%). " + " Проиграно ставок: " + str(loose)  + " (" + str(loose/games * 100) + "%). ")

# началась вторая стратегия, тоже стартуем с полной суммой
# вторая стратегия — с нулевым матожиданием
money = startmoney
# обнуляем статистику
win = 0
loose = 0

# начинаем играть с полной суммой
money = startmoney
# играем, пока есть деньги или пока мы не сыграем столько же игр, как и в первый раз
while (money > 0) and (win + loose < games):
    # ставка —  постоянная часть от первоначальной суммы
    bet = startmoney * c1
    # если ставка получилась больше, чем у нас осталось денег — ставим всё, что осталось, чтобы не уйти в минус
    if bet > money:
        bet = money
    # после ставки количество денег уменьшилось
    money -= bet
    
    # записываем очередную игру в статистику — деньги и номер игры 
    balance2.append(money)
    games2.append(len(games2)+1)

    # крутим рулетку, на которой 18 чёрных чисел, 18 красных. Так как всего поровну, матожидание будет равно нулю.
    # Ставим, как и в прошлом случае, на чёрное
    ball = random.randint(1,36)
    # пусть первые 18 будут чёрными — для простоты алгоритма
    # если наша ставка сыграла — мы попали в нужный диапазон
    if ball in range (1,19):
        # получаем назад нашу ставку в двойном размере
        money += bet * 2
        # увеличиваем количество побед
        win += 1
    else:
        # иначе — увеличиваем количество проигрышей
        loose += 1

# выводим результат игры по второй  стратегии
print("Выиграно ставок: " + str(win) + " (" + str(win/games * 100) + "%). " + " Проиграно ставок: " + str(loose)  + " (" + str(loose/games * 100) + "%). ")



# началась третья стратегия, тоже стартуем с полной суммой
# третья стратегия — с положительным матожиданием
money = startmoney
# обнуляем статистику
win = 0
loose = 0

# начинаем играть с полной суммой
money = startmoney
# играем, пока есть деньги или пока мы не сыграем столько же игр, как и в первый раз
while (money > 0) and (win + loose < games):
    # ставка —  постоянная часть от первоначальной суммы
    bet = startmoney * c1
    # если ставка получилась больше, чем у нас осталось денег — ставим всё, что осталось, чтобы не уйти в минус
    if bet > money:
        bet = money
    # после ставки количество денег уменьшилось
    money -= bet
    
    # записываем очередную игру в статистику — деньги и номер игры 
    balance3.append(money)
    games3.append(len(games3)+1)

    # крутим рулетку, на которой 18 чёрных чисел, 17 красных. Так как чёрных больше, а мы ставим на чёрное, то матожидание будет положительным
    # Ставим, как и в прошлом случае, на чёрное
    ball = random.randint(1,35)
    # пусть первые 18 будут чёрными — для простоты алгоритма
    # если наша ставка сыграла — мы попали в нужный диапазон
    if ball in range (1,19):
        # получаем назад нашу ставку в двойном размере
        money += bet * 2
        # увеличиваем количество побед
        win += 1
    else:
        # иначе — увеличиваем количество проигрышей
        loose += 1

# выводим результат игры по третьей  стратегии
print("Выиграно ставок: " + str(win) + " (" + str(win/games * 100) + "%). " + " Проиграно ставок: " + str(loose)  + " (" + str(loose/games * 100) + "%). ")

# строим графики
fig = go.Figure()
# для первой стратегии
fig.add_trace(go.Scatter(x=games1, y=balance1, name = "Отрицательное матожидание"))
# для второй
fig.add_trace(go.Scatter(x=games2, y=balance2, name = "Нулевое матожидание"))
# и для третьей
fig.add_trace(go.Scatter(x=games3, y=balance3, name = "Положительное матожидание"))
# выводим графики в браузер
fig.show()

Что дальше

Теперь у вас есть все инструменты, чтобы проверять результаты игр при любом матожидании. Этот код можно использовать не только для рулетки, а в принципе для любых игр, где результат определяется случайным образом.

Текст и код

Миша Полянин


Редактор

Максим Ильяхов


Корректор

Ира Михеева


Иллюстратор

Даня Берковский


Вёрстка

Маша Дронова


Доставка

Олег Вешкурцев

Любишь Python? Зарабатывай на нём!
Изучите самый модный язык программирования и станьте крутым бэкенд-разработчиком. Старт — бесплатно.
Попробовать бесплатно
Любишь Python? Зарабатывай на нём! Любишь Python? Зарабатывай на нём! Любишь Python? Зарабатывай на нём! Любишь Python? Зарабатывай на нём!
Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.
Начать карьеру в ИТ
Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию
Еще по теме
easy