Колода из 52 карт даёт ровно
C(52, 5) = 52! / (5! · 47!) = 2 598 960
различных пятикарточных рук, и каждая из них попадает ровно в одну категорию —
пара, флеш, каре и так далее. Поэтому вероятность любой комбинации — это просто
отношение числа рук этой категории к 2 598 960.
Точный расчёт: перебираем все 2 598 960 рук
Вместо того чтобы искать формулу для каждой комбинации отдельно, переберём
все сочетания 5 карт из 52 через itertools.combinations и для каждой
определим тип руки. Единственная тонкость — стрит «колесо» (А-2-3-4-5), где туз
считается младшей картой, и отдельная проверка на роял-флеш как частный случай
стрит-флеша.
Скачать Jupyter Notebook Открыть в Google Colab
from itertools import combinations
from collections import Counter
RANKS = '23456789TJQKA'
SUITS = 'CDHS'
DECK = [r + s for r in RANKS for s in SUITS]
def classify(hand):
rank_vals = sorted((RANKS.index(r) for r, s in hand), reverse=True)
suits = {s for r, s in hand}
counts = Counter(rank_vals)
count_vals = sorted(counts.values(), reverse=True)
is_flush = len(suits) == 1
unique_vals = sorted(set(rank_vals))
is_straight = (len(unique_vals) == 5 and unique_vals[-1] - unique_vals[0] == 4) \
or unique_vals == [0, 1, 2, 3, 12] # колесо А-2-3-4-5
is_royal = is_straight and is_flush and unique_vals == [8, 9, 10, 11, 12]
if is_royal:
return 'Роял-флеш'
if is_straight and is_flush:
return 'Стрит-флеш'
if count_vals == [4, 1]:
return 'Каре'
if count_vals == [3, 2]:
return 'Фулл-хаус'
if is_flush:
return 'Флеш'
if is_straight:
return 'Стрит'
if count_vals == [3, 1, 1]:
return 'Сет'
if count_vals == [2, 2, 1]:
return 'Две пары'
if count_vals == [2, 1, 1, 1]:
return 'Пара'
return 'Старшая карта'
result = Counter()
for hand in combinations(DECK, 5):
result[classify(hand)] += 1
total = sum(result.values())
order = ['Роял-флеш', 'Стрит-флеш', 'Каре', 'Фулл-хаус', 'Флеш', 'Стрит',
'Сет', 'Две пары', 'Пара', 'Старшая карта']
for name in order:
count = result[name]
odds = f'1 к {round(total / count):,}'
print(f'{name:14s} {count:>10,} {count/total:>8.4%} {odds}')
print(f'\nВсего рук: {total:,}')
Роял-флеш 4 0.0002% 1 к 649,740 Стрит-флеш 36 0.0014% 1 к 72,193 Каре 624 0.0240% 1 к 4,165 Фулл-хаус 3,744 0.1441% 1 к 694 Флеш 5,108 0.1965% 1 к 509 Стрит 10,200 0.3925% 1 к 255 Сет 54,912 2.1128% 1 к 47 Две пары 123,552 4.7539% 1 к 21 Пара 1,098,240 42.2569% 1 к 2 Старшая карта 1,302,540 50.1177% 1 к 2 Всего рук: 2,598,960
Перебор проходит все 2 598 960 рук без потерь и задвоений (сумма по категориям равна общему числу рук), а порядок, который получился по частоте, совпадает с официальным рангом комбинаций в покере. Это не совпадение: правила игры отражают именно эту таблицу.
| Комбинация | Число рук | Вероятность | Один шанс из |
|---|---|---|---|
| Роял-флеш | 4 | 0,0002 % | 649 740 |
| Стрит-флеш | 36 | 0,0014 % | 72 193 |
| Каре | 624 | 0,0240 % | 4 165 |
| Фулл-хаус | 3 744 | 0,1441 % | 694 |
| Флеш | 5 108 | 0,1965 % | 509 |
| Стрит | 10 200 | 0,3925 % | 255 |
| Сет | 54 912 | 2,1128 % | 47 |
| Две пары | 123 552 | 4,7539 % | 21 |
| Пара | 1 098 240 | 42,2569 % | 2 |
| Старшая карта | 1 302 540 | 50,1177 % | 2 |
Где интуиция обманывает
Флеш (пять карт одной масти) визуально выглядит «строже» стрита (пять карт подряд разных мастей) — кажется, что подобрать одну масть сложнее, чем подобрать последовательность. Перебор подтверждает именно это: стрит выпадает 10 200 раз, флеш — только 5 108, то есть флеш почти вдвое реже. Здесь интуиция и правила игры совпадают.
А вот что интуиция обычно упускает — это масштаб. 92,4 % всех возможных покерных рук — это старшая карта или одна пара (50,12 % + 42,26 %). Всё остальное — две пары, сет, стрит, флеш, фулл-хаус, каре, стрит-флеш — вместе занимает меньше 8 % пространства исходов. Именно поэтому одна пара на руках в реальной игре часто оказывается сильнее большинства других раздач: у подавляющего большинства случайных раздач нет даже пары.
Monte Carlo: сходится ли оценка к точному значению
Точный перебор возможен, потому что 2,6 миллиона комбинаций — это ещё подъёмный объём для компьютера. Для более сложных вопросов (например, вероятностей в Техасском холдеме с учётом чужих карт) перебор быстро становится неподъёмным, и вместо него используют случайную выборку — Monte Carlo. Проверим на уже известном ответе: раздадим случайные пятикарточные руки много раз подряд и посмотрим, как оценка вероятности фулл-хауса ведёт себя с ростом числа попыток.
import random
random.seed(42)
def random_hand():
return random.sample(DECK, 5)
exact = result['Фулл-хаус'] / total
print(f'Точная вероятность фулл-хауса: {exact:.4%}\n')
for n in [1_000, 10_000, 100_000, 1_000_000]:
hits = sum(1 for _ in range(n) if classify(random_hand()) == 'Фулл-хаус')
est = hits / n
print(f'{n:>9,} раздач: оценка {est:.4%} отклонение {abs(est - exact):.4%}')
Точная вероятность фулл-хауса: 0.1441%
1,000 раздач: оценка 0.1000% отклонение 0.0441%
10,000 раздач: оценка 0.1100% отклонение 0.0341%
100,000 раздач: оценка 0.1240% отклонение 0.0201%
1,000,000 раздач: оценка 0.1495% отклонение 0.0054%
Отклонение оценки от точного значения в целом убывает с ростом числа раздач — это закон больших чисел в действии, хотя убывание не гарантированно монотонно на каждом шаге: это случайный процесс, а не детерминированная сходимость. Monte Carlo даёт право на ошибку в обмен на то, что вообще не нужно перечислять все исходы — размен, который становится обязательным, как только точный перебор перестаёт помещаться в разумное время.
Та же комбинаторика — на столе, а не в коде
Термин «ауты» (outs) в живой игре — это то же самое подсчётное рассуждение, просто
без полного перебора. Если после флопа у игрока четыре карты одной масти, до флеша
не хватает одной карты, и она может быть любой из 9 оставшихся карт этой масти
среди 47 карт, которые ещё не видны (52 минус 5 своих и на столе). Вероятность
добрать флеш на следующей карте — 9 / 47 ≈ 19,1 %, тот же принцип
«нужный исход делить на все возможные», что и в переборе всей колоды, только для
одной конкретной ситуации, а не для всех рук сразу.
Чек-лист
- Можно ли явно перечислить все исходы? Тогда вероятность — это просто отношение «сколько подходит» к «сколько всего».
- Проверена ли полнота перебора — сумма по категориям равна общему числу исходов, ни один вариант не пропущен и не задвоен?
- Если перебор всех исходов физически неподъёмен — заменяется ли он случайной выборкой (Monte Carlo) с явной оценкой того, насколько оценка ещё может отклоняться от истинного значения?
- Совпадает ли результат подсчёта с интуицией — а если нет, то интуиция ошибается в структуре события или просто недооценивает масштаб?
Вывод
Ранжирование покерных комбинаций не результат договорённости игроков — это прямое следствие комбинаторики колоды, которое можно вывести с нуля перебором 2,6 миллиона рук. Там, где перебор всех исходов подъёмен, он даёт точный ответ без всяких предположений. Там, где неподъёмен — Monte Carlo даёт оценку, которая с ростом числа попыток приближается к тому же ответу, но остаётся приближением, а не точным значением.
Интуиция хорошо угадывает порядок редкости, но почти всегда врёт в масштабе — вот почему стоит хотя бы иногда пересчитывать то, что «и так понятно».