Проверяем кодом задачу про двойки и тузы
easy

Проверяем кодом задачу про двойки и тузы

Неожиданный результат

Недавно у нас вышла задача про колоду карт, которую мы решили как математики — с помощью теории вероятности. Теперь проверим, как это бьётся с практикой: напишем код на Python, который проведёт много экспериментов и покажет, какие там решения на самом деле.

Вот исходные задачи:

Первая: У нас есть стандартная колода из 52 игральных карт. Перемешиваем её, кладём рубашкой вверх и начинаем открывать карты по одной. Как только появился первый любой туз — открываем карты дальше. Какую карту мы после этого встретим с большей вероятностью — пикового туза или двойку треф?

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

Решим эти задачи по очереди в одном и том же скрипте на Python.

Как установить Python на компьютер и начать на нём писать

Собираем колоду карт

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

Логика такая:

  1. Берём список с названиями мастей.
  2. Берём список со значениями карт.
  3. В цикле по очереди объединяем по одному значению из этих двух списков и получаем полную колоду из 52 карт.

Сразу подключим нужные модули и переменные: сколько раз мы вытащили первой пикового туза, а сколько — трефовую двойку.

# подключаем модуль случайных значений
import random
# и модуль для копирования элементов
import copy

# списки с названиями мастей и значениями карт
suit = ['т','б', 'ч', 'п']
value = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'В', 'Д', 'К', 'Т', ]
# итоговый список карт в колоде
cards = []

# сколько раз вытащили первой каждую карту
ace = 0
dice = 0

# собираем колоду — для этого берём значение карты и масть
for i in value:
    for j in suit:
        # соединяем каждое значение с мастью
        cards.append(i + ' ' + j)

# выводим исходную колоду
print(cards)
# перемешиваем её
random.shuffle(cards)
# выводим перемешанную колоду
print(len(cards))

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

Описываем первую задачу

В первой задаче нам нужно сделать так:

  1. Перемешиваем карты.
  2. Открываем их по одной и смотрим, это туз или нет.
  3. Как только дошли до туза — смотрим по очереди остальные карты.
  4. Если после этого первой нам встретится двойка треф, то увеличиваем её счётчик, а если первым встретится пиковый туз — то его счётчик.
  5. Как только мы открыли одну из этих двух карт — останавливаемся.

Теперь запишем это всё на Python. На экране мы пока ничего нового не увидим, потому что нам нужно провести серию экспериментов, а не один.

# решаем первую задачу
def first(nc):
    # получаем доступ к глобальным переменным
    global ace, dice
    # перемешиваем карты
    random.shuffle(nc)
    # встретили первый туз или пока нет (на старте — нет)
    flag = False
    # открываем карты по одной
    for i in nc:
        # убираем только что открытую карту из колоды
        nc.pop(0)
        # если нам уже встретился первый туз до этого
        if flag:
            # если очередная карта — двойка треф, то увеличиваем её счётчик и выходим из цикла
            if i == '2 т':
                dice += 1
                break
            # если очередная карта — туз пик, то увеличиваем его счётчик и выходим из цикла
            if i == 'Т п':
                ace += 1
                break
        # если мы встречаем первого туза — меняем флаг на то, что туз уже был
        if 'Т' in i and not flag:
            flag = True

Запускаем серию экспериментов для первой задачи

Чтобы приблизить результаты к реальности, мы проведём этот эксперимент 10 000 раз. После этого посмотрим на значения счётчиков двойки и туза, будут ли они различаться и если да — то насколько. Теория говорит нам, что всё будет одинаково, но нужно проверить на практике.

# запускаем серию экспериментов
for s in range(10000):
    # делаем копию исходной колоды
    newCards = copy.deepcopy(cards)
    # запускаем функцию решения первой задачи
    first(newCards)

# выводим, сколько раз мы встретили первой двойку, а сколько — туза
print(dice, ace)

Оказывается, серия экспериментов нам показывает, что вероятность встретить первой двойку после туза выше как минимум на 10–15%. Для проверки проведём ещё 10 000 экспериментов:

Получается, что на практике вероятность встретить трефовую двойку выше, чем пикового туза.

Описываем вторую задачу

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

# сколько раз совпали карты после перемешивания
shuffleCount = 0

Вторая задача решается проще: мы просто перемешиваем колоду и по очереди сравниваем значения карт на одних и тех же местах в двух колодах — перемешанной и исходной. Если находим совпадение на каком-то месте — увеличиваем счётчик на единицу.

# решаем вторую задачу
def second(nc):
    # получаем доступ к глобальной переменной
    global shuffleCount
    # перемешиваем колоду
    random.shuffle(nc)
    # перебираем порядковые номера карт
    for i in range(52):
        # если в перемешанной колоде карта на очередной позиции совпадает с исходной — увеличиваем счётчик
        if nc[i] == cards[i]:
            shuffleCount += 1

Запускаем серию экспериментов для второй задачи

Сделаем то же самое, что и в первой задаче, — серию из 10 000 экспериментов — и посмотрим на результат. Теория говорит, что в среднем как минимум одна карта точно будет на том же месте после перемешивания. Чтобы это узнать, мы разделим количество совпавших мест на количество экспериментов (10 000):

# запускаем серию экспериментов
for s in range(10000):
    # делаем копию исходной колоды
    newCards = copy.deepcopy(cards)
    # запускаем функцию решения второй задачи
    second(newCards)
    
# выводим значение матожидания
print('✅', shuffleCount/10000)

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

# подключаем модуль случайных значений
import random
# и модуль для копирования элементов
import copy

# списки с названиями мастей и значениями карт
suit = ['т','б', 'ч', 'п']
value = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'В', 'Д', 'К', 'Т', ]
# итоговый список карт в колоде
cards = []

# сколько раз вытащили первой каждую карту
ace = 0
dice = 0

# сколько раз совпали карты после перемешивания
shuffleCount = 0

# собираем колоду — для этого берём значение карты и масть
for i in value:
    for j in suit:
        # соединяем каждое значение с мастью
        cards.append(i + ' ' + j)

# выводим исходную колоду
print(cards)
# перемешиваем её
random.shuffle(cards)
# выводим перемешанную колоду
print(cards)

# решаем первую задачу
def first(nc):
    # получаем доступ к глобальным переменным
    global ace, dice
    # перемешиваем карты
    random.shuffle(nc)
    # встретили первый туз или пока нет (на старте — нет)
    flag = False
    # открываем карты по одной
    for i in nc:
        # убираем только что открытую карту из колоды
        nc.pop(0)
        # если нам уже встретился первый туз до этого
        if flag:
            # если очередная карта — двойка треф, то увеличиваем её счётчик и выходим из цикла
            if i == '2 т':
                dice += 1
                break
            # если очередная карта — туз пик, то увеличиваем его счётчик и выходим из цикла
            if i == 'Т п':
                ace += 1
                break
        # если мы встречаем первого туза — меняем флаг на то, что туз уже был
        if 'Т' in i and not flag:
            flag = True

# запускаем серию экспериментов
for s in range(10000):
    # делаем копию исходной колоды
    newCards = copy.deepcopy(cards)
    # запускаем функцию решения первой задачи
    first(newCards)

# выводим, сколько раз мы встретили первой двойку, а сколько — туза
print(dice, ace)

# решаем вторую задачу
def second(nc):
    # получаем доступ к глобальной переменной
    global shuffleCount
    # перемешиваем колоду
    random.shuffle(nc)
    # перебираем порядковые номера карт
    for i in range(52):
        # если в перемешанной колоде карта на очередной позиции совпадает с исходной — увеличиваем счётчик
        if nc[i] == cards[i]:
            shuffleCount += 1

# запускаем серию экспериментов
for s in range(10000):
    # делаем копию исходной колоды
    newCards = copy.deepcopy(cards)
    # запускаем функцию решения второй задачи
    second(newCards)
    
# выводим значение матожидания
print('✅', shuffleCount/10000)

Обложка:

Алексей Сухов

Корректор:

Ирина Михеева

Вёрстка:

Мария Дронова

Соцсети:

Юлия Зубарева

Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.
Вам может быть интересно
easy
[anycomment]
Exit mobile version