跳至主要内容

使用 Python 探索扑克概率与统计

在 Python 中攻克概率与统计:了解排列与组合、相互依赖与相互独立事件以及期望值。
· 15分钟 阅读

使用 AI 探索

数据科学家构建机器学习模型来进行预测并优化决策。在网络扑克中,选择是下注、跟注还是弃牌。不过,您不被允许使用软件来做出这些决定——大多数在线扑克网站的规则都会在此划线。既然不能训练机器学习模型,您就必须训练自己的大脑。这需要在牌桌之外不断进行权益(equity)计算,而这些计算会用到许多不同的概率与统计概念。

在本教程中,您将用一副扑克牌和通用的扑克场景学习其中的一些概念。

Poker Card Banner

如果您有兴趣用 Python 掌握统计学,推荐学习 DataCamp 的《Statistical Thinking in Python》课程。

个人动机

有好几年时间,我以职业玩家的身份靠在线打扑克为生。对我而言,转向数据科学是很自然的一步,因为它与在线扑克盈利所需的技能十分相似。我写过一篇博客介绍数据科学与扑克的共通之处,并提到在在线扑克网站上每打一手牌,都会生成一份手牌记录。这些记录说明了该手牌中每位玩家的所有操作。我使用名为Hold’em Manager的软件(可以把它想象成扑克界的 Tableau)来利用这些数据。Hold'em Manager 会将每份手牌记录实时下载到 PostgreSQL 数据库,便于您追踪对手的倾向。

概率论:导论

在正式上手之前,先来思考什么是概率论,以及当您进入数据科学领域时,为什么有必要学习它。此外,您还将了解一些关键概念,它们在整个教程中都会派上用场,并学习如何计算单个事件的概率。

在现实生活中,您经常会好奇某件事发生的概率是多少,比如中乐透、您支持的足球队取胜,或者心仪鞋款的折扣。“有多大机率……”这类表达您大概经常会用。判断某个事件发生的机会大小,就称为“概率”。

这种概率与数学视角下的概率不同,后者属于数学的一个分支——概率论。在数学中,对“概率”有两大类解释——“物理概率”和“证据概率”。

前者也称客观或频率概率,与掷硬币、转轮盘、掷骰子等随机物理系统相关。在这类系统中,在大量试验下,某类事件会以一个稳定的比率,或称“相对频率”,发生。

后者也称贝叶斯概率,可以赋予任何陈述,即使没有随机过程参与,用来表示其主观可信度,或该陈述得到现有证据支持的程度。在多数论述中,证据概率被视为信念程度,可用在某些赔率下进行下注的倾向来定义。

注意 概率论主要关注预测未来事件发生的可能性,而统计学则分析过去事件的发生频率。这也解释了为什么概率论是立志成为数据科学家必须掌握的核心主题之一:众所周知,在数据科学和机器学习中,您会用已发生事件的数据来预测未来事件。

因此,尽管概率论往往不易凭直觉理解,但其概念对于数据科学与预测分析至关重要。

关键概念与符号

为本教程需要,您应当了解的一些关键概念主要来自频率主义的概率观,因为本教程以一副 52 张的扑克牌为例。

从这一视角看,概率论的基本要素是一个至少在理论上可在基本相同条件下重复进行的实验。该实验在不同的试次或单次执行中可能产生不同结果。实验所有可能结果的集合称为“样本空间”。一个事件是样本空间的某个定义明确的子集。

这些都相当理论化。

我们来看一些例子:

  • 一个有点老套的例子是抛硬币。在这种情况下,实验就是抛硬币。您可以抛很多次,每次试验可能有不同结果。由于有两个可能的结果——正面或反面——样本空间大小为 2。然而,“抛硬币”这个事件,例如可以只包含一个结果“正面”。同样地,当您抛两次硬币时,您的事件“第一次为正面”可能对应“正-正”或“正-反”。
  • 另一个也许不那么直观的例子是转地球仪并用手指按停。您可以多次旋转,每次可能有不同的结果——您的手指要么落在陆地,要么落在海洋。也就是说样本空间为 2。事件“我的手指在陆地上”可能对应“陆-海”或“陆-陆”。
  • 最后一个例子是掷骰子的实验。您可以掷多次,每次的结果可能不同:确切地说有 6 种,因为骰子面数为 6(1,2,3,4,5,6)。事件“两次掷骰之和等于 10”可以由若干和为 10 的结果组成,而事件“点数为偶数”可以包含 2、4 或 6。

了解了本教程将用到的关键概念后,也来看看将会遇到的一些概率符号:

符号含义
$$\cap$$且
$$\cup$$或
|在…条件下(给定)

计算单个事件的概率

现在您已经准备就绪,可以开始确定单个事件发生的概率,例如硬币落反面的概率。要计算该概率,需用事件可能结果的数量除以样本空间大小。

这意味着您要先考虑硬币落反面有几种可能结果,以及总共有几种可能结果。前者是 1,因为只有一种方式得到反面。后者是 2,因为抛硬币要么正面要么反面。

总结来说,事件 A 的概率计算如下:

$$P(A) = \frac{Event \ outcomes \ favorable \ to \ A}{Sample \ space}$$

在抛硬币的例子中,落反面的概率是 1/2 或 0.5。

注意 概率总在 0 到 1 之间,0 表示事件几乎不可能发生,1 表示事件必然发生。

现在看第二个计算事件概率的例子。

Statistics Sample Space for Deck of Cards

一副标准扑克牌有 52 张,其中 4 张是 A。如果沿用上面的抛硬币思路来求“抽到 A”的概率,您要用事件可能结果数量(4)除以样本空间(52):

$$P(A) = \frac{4}{52}$$

注意 这里的 $A$ 表示“抽到一张 A”这一事件。

现在,用 Python 来求抽到 A 的概率:

# Sample Space
cards = 52

# Outcomes
aces = 4

# Divide possible outcomes by the sample set
ace_probability = aces / cards

# Print probability rounded to two decimal places
print(round(ace_probability, 2))
0.08

从一副标准牌中抽到 A 的概率是 0.08。若要转换为百分比,只需乘以 100。

# Ace Probability Percent Code
ace_probability_percent = ace_probability * 100

# Print probability percent rounded to one decimal place
print(str(round(ace_probability_percent, 0)) + '%')
8.0%

抽到 A 的百分比概率是 8%。

既然已经看过两个计算概率的例子,您很容易想到进一步拓展,例如求“抽到红心”“抽到人头牌(J、Q、K)”,或两者的组合,比如“红心 Q”的概率。

在这种情况下,您可以创建一个用户自定义函数(UDF)event_probability(),传入event_outcomes和sample_space来得到事件的百分比概率,因为您会多次复用相同的代码:

# Create function that returns probability percent rounded to one decimal place
def event_probability(event_outcomes, sample_space):
    probability = (event_outcomes / sample_space) * 100
    return round(probability, 1)

# Sample Space
cards = 52

# Determine the probability of drawing a heart
hearts = 13
heart_probability = event_probability(hearts, cards)

# Determine the probability of drawing a face card
face_cards = 12
face_card_probability = event_probability(face_cards, cards)

# Determine the probability of drawing the queen of hearts
queen_of_hearts = 1
queen_of_hearts_probability = event_probability(queen_of_hearts, cards)

# Print each probability
print(str(heart_probability) + '%')
print(str(face_card_probability) + '%')
print(str(queen_of_hearts_probability) + '%')
25.0%
23.1%
1.9%

这些结果大概不会让您惊讶:正如预期,抽到“红心 Q”的机会远小于抽到普通人头牌或红心。

组合与排列下的概率

您已经在上一节看到,确定样本空间的大小是计算概率的关键。然而,这有时并不容易!

幸运的是,有一些方法可以简化计数工作,其中两种就是排列与组合。本节将介绍它们的确切含义,以及如何用它们来计算样本空间的大小!

排列(Permutations)

排列是指从给定集合中,在通常不放回的情况下,按指定大小选取并排列子集的方式数。一个例子是 4 位数且不重复数字的 PIN 码。没有重复数字的可能数可通过如下计算得到:

$$10 \times 9 \times 8 \times 7$$。

您有 10 个数字可选,但由于是不放回抽取,每选一个,下一位可选项就少一个。这意味着选 PIN 的第一位有 10 个选择(0 到 9),第二位有 9 个,依此类推。

更高层面看,上述实际上考虑了两点:(1)可选择的数字总数;(2)实际选择的位数。在上例中,10 是可选择的数字总数(0 到 9),而实际选择的是 4 位。

计算排列时,意味着您考虑可选数字的全集,其实是 $$10 \times 9 \times 8 \times 7 \times 6 \times 5 \times 4 \times 3 \times 2 \times 1$$,再将其除以“可选总数(10)”与“实际选择数(4)”的差所对应的连乘。因为是在概率情境下,这个差对应的是 $$6 \times 5 \times 4 \times 3 \times 2 \times 1$$。

注意 您也可以写成:

$$10P4 = \frac{10!}{(10 - 4)!}$$

您会注意到式子中有“10!”与“6!”(即“10 的阶乘”和“6 的阶乘”),表示从 1 到 10 或 6 的所有正整数相乘。

该计算结果是 5040 种排列。注意 这与您之前直接用 10、9、8、7 相乘得到的结果完全一致。

将以上一般化,计算排列的公式为:

$$nPk = \frac{n!}{(n - k)!}$$

我们用一个例子来练习!

要找口袋 A(两张 A)的排列数,且只选 2 张,您会考虑可选的 A 的全集(4),以及实际选择的张数(2):

$$4P2 = \frac{4!}{(4 - 2)!}$$

组合(Combinations)

您看到过在排列中,顺序是重要的;而在组合中,顺序不重要。组合指从给定集合中按指定大小选取子集的方式数。

例如,您有一副 52 张的扑克牌。从中取出 3 张。可以有多少种不同的选法?

直觉上应为 $$52 \times 51 \times 50$$,这其实与刚才使用的排列公式相同!不过在组合中,不考虑顺序。这意味着若要得到实际的组合数,只需先生成所有排列数,再除以所有冗余的排列数,即 $$3 \times 2 \times 1$$。

因此组合的计算如下:

$$52C3 = \frac{\frac {52!}{(52-3)!}}{3!}$$

这可以一般化为以下公式:

$$nCk = \frac{nPk}{k!}$$

可以清楚看到,分子正是刚才的排列公式,而分母是所选张数的阶乘。

再看一个关于 A 的例子。一副牌有四张 A,所有不同的口袋 A 组合如下:

  1. 红心 A / 方片 A
  2. 红心 A / 梅花 A
  3. 红心 A / 黑桃 A
  4. 方片 A / 梅花 A
  5. 方片 A / 黑桃 A
  6. 梅花 A / 黑桃 A

口袋 A 一共有 6 种组合。要找组合数,先求排列数:

# Permutations Code
import math
n = 4
k = 2

# Determine permutations and print result
Permutations = math.factorial(n) / math.factorial(k)
print(Permutations)
12.0

要得到组合数,只需用排列数除以子集大小的阶乘。试着求德州扑克起手两张牌的组合总数。

$$52C2 = \frac{52P2}{2!}$$

# Combinations Code
n = 52
k = 2

# Determine Permutations
Permutations = math.factorial(n) / math.factorial(n - k)

# Determine Combinations and print result
Combinations = Permutations / math.factorial(k)
print(Combinations)
1326.0

相互独立与相互依赖事件

在引言中您读到,事件是样本空间的一个定义明确的子集。事件可分为两类:相互独立或相互依赖。

相互独立事件是指彼此不会影响对方概率的事件。如果知道事件 A 是否发生,对事件 B 是否发生就没有提供任何信息,则事件 A 与 B 独立。

例如,您从牌堆抽一张 A,把牌放回去,洗牌,然后再抽一次。第一次抽到 A 的概率与第二次相同。

相互依赖事件则会影响另一事件的概率。

例如,您先从牌堆抽一张牌,然后在不放回第一张的情况下再抽第二张。在这种情况下,第一次抽到 A 的概率与第二次抽到 A 的概率不同。第一张抽出后,样本空间从 52 减少到 51。根据第一张是什么牌,事件可能结果的数量也会改变。如果第一张是 A,那么第二次只剩 3 张 A。

现在用更形式化的方式来表达。若事件 A 与 B(且其概率非零)独立,当且仅当下列等价陈述之一成立:$$P (A ∩ B) = P(A)P(B)$$ $$P (A|B) = P(A)$$ $$P (B|A) = P(B)$$

换句话说,事件 A 与 B 独立当且仅当:

  • A 与 B 同时发生的概率等于它们各自发生概率的乘积。
  • 在 B 已发生的条件下,A 发生的概率等于 A 自身发生的概率。
  • 在 A 已发生的条件下,B 发生的概率等于 B 自身发生的概率。

来看下面的例子:您已经知道第一次抽到 A 的概率。现在需要确定当第一张是 K 或 A 时,第二次抽到 A 的概率:

# Sample Space
cards = 52
cards_drawn = 1 
cards = cards - cards_drawn 

# Determine the probability of drawing an Ace after drawing a King on the first draw
aces = 4
ace_probability1 = event_probability(aces, cards)

# Determine the probability of drawing an Ace after drawing an Ace on the first draw
aces_drawn = 1
aces = aces - aces_drawn
ace_probability2 = event_probability(aces, cards)

# Print each probability
print(ace_probability1)
print(ace_probability2)
7.8
5.9

在扑克中,有一些常见情形与相互依赖事件的概念相关。

在开始之前,先做一点背景介绍。这里讨论的游戏是德州扑克。使用标准 52 张牌,德州扑克是最流行的扑克变体。每位玩家起手会被发两张牌,最终将用这两张牌与过程中发出的五张公共牌组合,凑出最好的五张牌。发牌分四轮:

  • 翻牌前(Pre-Flop):每位玩家得到两张称为“底牌”的手牌
  • 翻牌(Flop):发三张公共牌
  • 转牌(Turn):再发一张公共牌
  • 河牌(River):最后一张公共牌

相互依赖事件:同花听牌

您的手牌

cards

公共牌

cards

现在是转牌圈(Turn),您已有四张同花听到 A 高同花。同花是五张牌花色相同的强牌。最后一张公共牌(称为河牌)是方片的概率是多少?

# Sample Space
cards = 52
hole_cards = 2
turn_community_cards = 4
cards = cards - (hole_cards + turn_community_cards)

# Outcomes
diamonds = 13
diamonds_drawn = 4
# In poker, cards that complete a draw are known as "outs"
outs = diamonds - diamonds_drawn

#Determine river flush probability
river_flush_probability = event_probability(outs, cards)
print(river_flush_probability)
19.6

河牌击中同花的概率大约为 20%。再看一个:

相互依赖事件:两头顺听牌

您的手牌

cards

公共牌

cards

现在是转牌圈,您有两头顺听牌。顺子也是一手强牌,由五张点数连续的牌组成。之所以是两头顺,是因为任何 8(8、9、10、J、Q)或任何 K(9、10、J、Q、K)都能完成顺子。

河牌完成顺子的概率是多少?

# Sample Space
cards = 52
hole_cards = 2
turn_community_cards = 4
cards = cards - (hole_cards + turn_community_cards)

# Outcomes
eights = 4
kings = 4
outs = eights + kings

# Determine river straight probability
river_straight_probability = event_probability(outs, cards)
print(river_straight_probability)
17.4

河牌击中顺子的概率大约为 17%。

多个事件

目前为止,您在计算概率时只考虑了单个事件,那么遇到多个事件时该怎么办?

一个多个事件的例子是这样的问题:“从装有燕麦饼干和巧克力豆饼干的饼干罐中吃四块,先后吃到三块燕麦饼干再吃一块巧克力豆饼干的概率是多少?”吃四块实际上就是四个事件。

要计算多个事件的概率,基本做法是确定事件的个数(本例为 4),分别求出每个事件发生的概率,然后将这些概率相乘得到最终答案。在上述例子中,就是 0.5 × 0.5 × 0.5 × 0.5,即 0.0625。

$$P(Event A \cap Event B)=P(Event A) \times P(Event A)$$

注意 在这里,您计算的是“吃到一块燕麦饼干 且 又一块燕麦饼干 且 第三块燕麦饼干 且 最后一块巧克力豆饼干”的概率。当您考虑的是必须全部发生的事件时,就将概率相乘。

对一副扑克牌,您可以问“在不放回的情况下抽三张,都是红心的概率是多少?”当不放回抽取时,表示抽出一张后不放回,最终选择中不会包含同一张牌。在这种情况下,概率计算为 13/52 × 12/51 × 11/50。

互斥事件

在处理多个事件时,您也可能遇到互斥(不相交)事件:它们不可能同时发生。在这种情况下,若要计算多个互斥事件中任一事件发生的概率(即并集),不需要相乘,而是把各事件发生的概率直接相加:

$$P(Event A \cup Event B) = P(Event A) + P(Event B)$$

关键在于理解这里的“或(OR)”非常重要:抽到红心 或 抽到梅花是两个互斥事件。红心就是红心,梅花就是梅花。要计算“抽到红心或梅花”的概率,将“抽到红心”的概率与“抽到梅花”的概率相加即可。

$$P(Heart \cup Club) = (\frac{13}{52}) + (\frac{13}{52})$$

现在请您计算以下互斥事件的概率:

  1. 抽到红心或抽到梅花;
  2. 抽到 A、K 或 Q。
# Sample Space
cards = 52

# Calculate the probability of drawing a heart or a club
hearts = 13
clubs = 13
heart_or_club = event_probability(hearts, cards) + event_probability(clubs, cards)

# Calculate the probability of drawing an ace, king, or a queen
aces = 4
kings = 4
queens = 4
ace_king_or_queen = event_probability(aces, cards) + event_probability(kings, cards) + event_probability(queens, cards)

print(heart_or_club)
print(ace_king_or_queen)
50.0
23.1

非互斥事件

并非所有事件都互斥:抽到红心或抽到 A 是两个非互斥事件。红心 A 既是 A 也是红心。当事件不互斥时,必须校正重叠部分。

$$P(Event A \cup Event B) = P(Event A) + P(Event B) - P(EventA \cup EventB)$$

要计算“抽到红心或 A”的概率,将“抽到红心”的概率与“抽到 A”的概率相加,再减去“抽到红心 A”的概率。

$$P(Heart \cup Ace) = (\frac{13}{52}) + (\frac{4}{52}) - (\frac{1}{52})$$

计算下列非互斥事件的概率:

  1. 抽到红心或 A;
  2. 抽到红牌或人头牌。
# Sample Space
cards = 52

# Calculate the probability of drawing a heart or an ace
hearts = 13
aces = 4
ace_of_hearts = 1
heart_or_ace = event_probability(hearts, cards) + event_probability(aces, cards) - event_probability(ace_of_hearts, cards)

# Calculate the probability of drawing a red card or a face card
red_cards = 26
face_cards = 12
red_face_cards = 6
red_or_face_cards = event_probability(red_cards, cards) + event_probability(face_cards, cards) - event_probability(red_face_cards, cards)

print(round(heart_or_ace, 1))
print(round(red_or_face_cards, 1))
30.8
61.6

独立事件的交集

两个独立事件的交集概率,等于各自发生概率的乘积。

$$P(Event A \cap Event B) = P(Event A) \times P(Event B)$$

如果您想知道从牌堆抽到一张 A,放回并洗牌后再抽到一张 A 的概率,就将“抽到 A”的概率乘以“抽到 A”的概率。

$$P(Ace \cap Ace) = (\frac{4}{52}) \times (\frac{4}{52})$$

# Sample Space
cards = 52

# Outcomes
aces = 4

# Probability of one ace
ace_probability = aces / cards

# Probability of two consecutive independant aces 
two_aces_probability = ace_probability * ace_probability

# Two Ace Probability Percent Code
two_ace_probability_percent = two_aces_probability * 100
print(round(two_ace_probability_percent, 1))
0.6

在独立的情况下,连续抽到两张 A 的概率为 0.592%。如果第二个事件是依赖的呢?

依赖事件的交集

两个非独立事件(事件 A 与事件 B 在 A 已发生条件下)的交集概率,等于事件 A 的概率乘以“在 A 已发生条件下 B 的条件概率”。

$$P(Event A \cap Event B | A) = P(Event A) \times P(Event B | A)$$

德州扑克中最好的起手牌是口袋 A。被发到两张 A 的概率是多少?

$$P(Ace \cap Ace | Ace) = (\frac{4}{52}) \times (\frac{3}{51})$$

# Sample Space first draw
cards = 52

# Outcomes first draw
aces = 4

# Probability of ace on first draw
first_ace_probability = aces / cards

# Sample Space second draw
cards = cards - 1

# Outcomes second draw
aces = aces - 1

# Probability of ace on second draw after ace on first
second_ace_probability = aces / cards

# Probability of two consecutive aces (dependent)
both_aces_probability = first_ace_probability * second_ace_probability * 100
print(both_aces_probability)
0.4524886877828055

在依赖的情况下,连续抽到两张 A 的概率为 0.452%。我们来看看牌桌上与此相关的几个场景。

依赖事件的交集:翻牌圈同花听牌

您的手牌

cards

公共牌

cards

这与前面的同花听牌类似,只是这次是在翻牌圈(Flop),还会发两张公共牌而不止一张。如何求到河牌形成同花的概率?首先需要列出所有可能情景:

  • A) 转牌是方片,河牌不是方片(成同花)
  • B) 转牌不是方片,河牌是方片(成同花)
  • C) 转牌是方片,河牌是方片(成同花)
  • D) 转牌不是方片,河牌也不是方片(未成同花)

只有这四种可能,且彼此互斥。这意味着如果把每种情景发生的概率相加,总和为 1。换言之,这四种情景必有其一发生。您想知道 A、B 或 C 发生的概率。最简单的思路是先求情景 D 的概率,再用 1 减去它。

# Sample Space on turn
cards = 52
hole_cards = 2
flop_community_cards = 3
cards = cards - (hole_cards + flop_community_cards)

# Outcomes
diamonds = 13
diamonds_drawn = 4
non_diamonds_drawn = 1
outs = diamonds - diamonds_drawn
turn_non_diamonds = cards - outs - non_diamonds_drawn 

# Probability of not getting a diamond on the turn
no_diamond_turn_probability = turn_non_diamonds / cards

# Sample Space on river
turn_community_card = 1
cards = cards - turn_community_card

# Outcomes on river
river_non_diamonds = turn_non_diamonds - turn_community_card

# Probability of not getting a diamond on the river
no_diamond_river_probability = river_non_diamonds / cards

# Probability of not getting a flush
no_flush_probability = no_diamond_turn_probability * no_diamond_river_probability

# Probability of getting a flush
flush_probability = 1 - no_flush_probability
flush_probability_percent = flush_probability * 100

# Print probability percent rounded to one decimal place
print(round(flush_probability_percent, 1))
38.4

现在稍微改变一下牌面:

您的手牌

cards

公共牌

cards

这与上一个情景类似,但有一个重要区别。您仍然是同花听牌,但这次您没有 A。如果转牌与河牌都是方片,很可能有人会有更大的同花。请计算“转牌或河牌出现方片,但不是两张都出现”的概率。您已经有未成同花的概率了。

现在再求“转牌与河牌都出现方片”的概率,将其与“未成同花”的概率相加,再用 1 减去即可。

# Sample Space on turn
cards = 52
hole_cards = 2
flop_community_cards = 3
cards = cards - (hole_cards + flop_community_cards)

# Outcomes on turn
diamonds = 13
diamonds_drawn = 4
outs = diamonds - diamonds_drawn

# Probability of diamond on turn
diamond_turn_probability = outs / cards

# Sample Space on river
turn_diamonds = 1
cards = cards - turn_diamonds

# Outcomes on river
river_diamonds = outs - turn_diamonds

# Probability of diamond on river
diamond_river_probability = river_diamonds / cards

# Probability of getting a diamond on the turn and river
two_diamonds_probability = diamond_turn_probability * diamond_river_probability

# Determine the probability of getting only one diamond by the river
one_diamond_probability = 1 - (no_flush_probability + two_diamonds_probability)
one_diamond_probability_percent = one_diamond_probability * 100

# Print probability percent rounded to one decimal place
print(round(one_diamond_probability_percent, 1))
35.1

期望值(Expected Value)

在玩扑克这样的游戏时,您会非常关心这样的问题:“如果我反复玩这局游戏,平均会赚(或赔)多少?”对扑克而言,尤其是职业玩家,这一点更是如此!

如果游戏的可能结果及其对应概率可以用一个随机变量描述,那么就可以通过计算其期望值来回答上述问题。期望值等于结果的加权平均数,每个结果以其概率为权重。

换句话说,只需用总收益乘以获胜概率即可得到期望值:

$$Expected Value = Total Value \times Probability$$

如果底池有 \$100(总收益),您赢下底池的概率为 0.75,那么期望值是多少?

$$Expected Value = \$100 \times 0.75$$

# Initialize `pot` and `probability` variables
pot = 100
probability = 0.75

# Determine expected value
expected_value = pot * probability
print(expected_value)
75.0

您的期望值是 \$75。期望值是扑克中的重要概念。让我们回到第一个同花听牌的例子,看看如何利用期望值为自己争取优势。

您的手牌

cards

对手的手牌

cards

公共牌

cards

  • 底池总额 = \$60

  • 对手下注 = \$20

您的对手好心把他的手牌亮出来,是一对 2。要在河牌赢下这手牌,您必须击中任意方片,但不能是 J 或 2。因为方片 J 或方片 2 会让对手形成更大的牌型,分别是葫芦和四条。您需要跟注 \$20 才能继续看牌,若赢下手牌可赢得 \$60。如果您的期望值大于 \$20,就应该跟注;否则应当弃牌。

来判断您是否应该跟注:

# Sample Space
cards = 52
hole_cards = 2
# Your opponent provided you information... use it!
opponents_hole_cards = 2 

turn_community_cards = 4
cards = cards - (hole_cards + opponents_hole_cards + turn_community_cards)

# Outcomes
diamonds = 13
diamonds_drawn = 4

# You can't count the two diamonds that won't help you win
diamond_non_outs = 2 

outs = diamonds - diamonds_drawn - diamond_non_outs

# Determine win probability
win_probability = outs / cards

# Determine expected value
pot = 60
ev = pot * win_probability

# Print ev and appropriate decision
call_amount = 20
if ev >= 20:
    print(round(ev, 2), 'Call')
else:
    print(round(ev, 2), 'Fold')
9.55 Fold

您的期望值只有 \$9.55,低于为获得该回报所需承担的 \$20 风险,因此必须弃牌。理解期望值对牌桌决策至关重要,第二部分将更深入探讨这一概念。

结语

恭喜,您已完成本次关于用 Python 学习概率论的教程!这就结束了教程的第一部分。您学习了多个核心概率概念,包括独立/依赖事件、排列/组合、多个事件、期望值,以及如何计算它们。

在第二部分,您将把这些概念应用到我职业生涯中打过的真实手牌上。

*在翻牌、转牌与河牌之前,会发一张面朝下的“烧牌(Burn)”。由于这张牌面朝下,且无人知道其点数,因此不计作一次试验。

欢迎查看 DataCamp 的《Python 中的概率分布》教程。

主题