简单的统计学:如何用Python计算扑克概率

   日期:2020-05-21     浏览:132    评论:0    
核心提示:介绍​ 在本文中,我们展示了如何在Python中表示基本的扑克元素,例如“手”和“组合”,以及如何计算扑克赔率,即在无限额德州扑克中获胜/平局/失败的可能性。​ 我们根据《拉斯维加斯威尼斯之夜》中的真实故事提供实用的分析。在内华达州拉斯维加斯的威尼斯人的一天。​ 我们将使用poker包来表示手牌,连击和范围。 我已经扩展了来自Kevin Tseng的扑克赔率计算器,因此它除了能够计算单个手牌之外,还可以基于范围(可能的手牌)来计算扑克概率。from poker import Rangefpython

介绍

​ 在本文中,我们展示了如何在Python中表示基本的扑克元素,例如“手”和“组合”,以及如何计算扑克赔率,即在无限额德州扑克中获胜/平局/失败的可能性。

​ 我们根据《拉斯维加斯威尼斯之夜》中的真实故事提供实用的分析。

在内华达州拉斯维加斯的威尼斯人的一天。

​ 我们将使用poker包来表示手牌,连击和范围。 我已经扩展了来自Kevin Tseng的扑克赔率计算器,因此它除了能够计算单个手牌之外,还可以基于范围(可能的手牌)来计算扑克概率。

from poker import Range
from poker.hand import Combo

import holdem_calc
import holdem_functions

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from IPython.core.display import display, HTML

hero_odds = []
hero_range_odds = []

翻牌圈

​ 我的手牌为K和J(KJ),我使用来自poker.hand的Combo类构造我的手牌。

# my hand = King of spades and Jack of clubs
hero_hand = Combo('KsJc')
print(hero_hand)

​ 我记不清翻牌前发生的事情以及我的位置。 但是,我确实记得翻牌前有加注,而翻牌后只剩下两名选手:我和对方。

​ 我们现在要注意。 翻牌圈出现梅花Q,红桃10和梅花J。 是的,我翻到了顺子!

​ 让我们假设没有对方扑克的先验知识来计算翻牌后的赔率,即在翻牌后,我们将计算出我的牌胜过随机的一对牌的可能性。

flop = ["Qc", "Th", "9s"] # the flop
board = flop # the board equals the flop
villan_hand = None # no prior knowledge about the villan
exact_calculation = True #  calculates exactly by simulating the set of all possible hands
verbose = True # returns odds of making a certain poker hand, e.g., quads, set, straight
num_sims = 1 # ignored by exact_calculation = True
read_from_file = None # we are not reading hands from file


odds = holdem_calc.calculate_odds_villan(board, exact_calculation, 
                            num_sims, read_from_file , 
                            hero_hand, villan_hand, 
                            verbose, print_elapsed_time = True)

​ Holdem_calc中的函数calculate_odds_villan可以计算出特定的德州扑克赢手的概率。 通过运行蒙特卡洛方法可以估算出该概率,也可以通过模拟所有可能的情况来准确地计算出该概率,快速计算翻牌后的确切赔率。因此在这里我们不需要蒙特卡洛近似值。 这是我们的赔率:

odds[0]

{'tie': 0.04138424018164999, 'win': 0.9308440557284221, 'lose': 0.027771704089927955}

​ 此时,我感觉还不错。 在随机的情况下,我只有2.77%的机会输,获胜的机会超过93%。 这很乐观。

​ 考虑到翻牌前有加注,而只有我和对方在翻牌后才离开,所以对方有一些手牌,对吧? 我们称这种可能的手为范围。 这是我们根据几个因素(包括对方的举止,位置,下注大小等)做出的推论。该推论导致我们假设对方可能拥有一组手牌。 在这一点上,我认为对方有:

  • 一对7或更好
  • A /10或更好
  • K/J或更好

​ 我们可以使用“类别范围”来表示该范围,如下所示:

villan_range = Range('77+, AT+, KJ+')
display(HTML(villan_range.to_html()))
print("#combo combinations:" + str(len(villan_range.combos)))

​ 这使对方手牌组合从总共51 * 52–1 = 2651个可能减少到144种可能。 现在假设对方手牌的范围来计算我的赔率。

items = [holdem_calc.calculate_odds_villan(board, exact_calculation, 
                            num_sims, read_from_file , 
                            hero_hand, villan_hand, 
                            verbose, print_elapsed_time = False) for villan_hand in villan_range.combos]

odds = {}
[odds.update({odd_type: np.mean([res[0][odd_type] for res in items if res])}) for odd_type in ["tie", "win", "lose"]]
{'tie': 0.11423324150596878, 'win': 0.8030711151923272, 'lose': 0.08269564330170391}

​ 在假定的范围内,我的获胜几率从93%下降至80%。 但是,我仍然很可能损失8.2%。 在这一点上,我很明确。 但是我应该继续吗? 我绝对希望对方继续比赛并且不弃牌。 但是他在翻牌后有个好牌的可能性有多大? 让我们看看如果我们继续玩到最后,他伸手的几率是多少。

for hand_ranking in holdem_functions.hand_rankings:
    print(hand_ranking +": " + str(np.mean([res[1][1][hand_ranking] for res in items if res])))
High Card: 0.06978879706152433 
Pair: 0.3662891541679421 
Two Pair: 0.23085399449035812 
Three of a Kind: 0.09733700642791548 
Straight: 0.18498112437506367 
Flush: 0.0040608101214161816 
Full House: 0.04205693296602388 
Four of a Kind: 0.004560759106213652 
Straight Flush: 2.0406081012141617e-05 
Royal Flush: 5.101520253035404e-05 

​ 如果我们继续玩,对方很有可能做出一对(36%)或两对(23%)。 他极有可能直接命中(18%)甚至打出盘(9.7%)或满堂(4%)。 由于对方很有可能拥有合理的手牌,因此我决定下高注,大约底池的2/3。

转牌

​ 到转牌了,是方片2(2)。 基本上,这是一张空白牌,也就是说,它对我们的游戏没有太大影响。

turn= ["2d"]
board = flop + turn
villan_hand = None

odds = holdem_calc.calculate_odds_villan(board, exact_calculation, 
                            num_sims, read_from_file , 
                            hero_hand, villan_hand, 
                            verbose, print_elapsed_time = True)
hero_odds.append(odds[0]['win'])

print(odds[0])
{'tie': 0.0233201581027668, 'win': 0.9677206851119895, 'lose': 0.008959156785243741}

​ 假设对方的牌是随机的,那么我现在有96%的获胜几率。

​ 但是,考虑到我假定的对方手牌范围,我的获胜几率现在从翻牌时的80%上升到86%。 我再次下注,对方跟注,河牌来了。

items = [holdem_calc.calculate_odds_villan(board, exact_calculation, 
                            num_sims, read_from_file , 
                            hero_hand, villan_hand, 
                            verbose, print_elapsed_time = False) for villan_hand in villan_range.combos]

odds = {}
[odds.update({odd_type: np.mean([res[0][odd_type] for res in items if res])}) for odd_type in ["tie", "win", "lose"]]
{'tie': 0.10123966942148759, 'win': 0.8615702479338843, 'lose': 0.0371900826446281}

河牌

​ 是梅花K(K)。 这使对方更容易胜利。 所以这对我来说是个坏消息。

river = ["Kc"]
board = flop + turn + river 
verbose = True

villan_hand = None

odds = holdem_calc.calculate_odds_villan(board, exact_calculation, 
                            num_sims, read_from_file , 
                            hero_hand, villan_hand, 
                            verbose, print_elapsed_time = True)
hero_odds.append(odds[0]['win'])

print(odds[0])
{'tie': 0.11818181818181818, 'win': 0.8696969696969697, 'lose': 0.012121212121212121}

​ 现在,我对随机牌的获胜几率从96%降至约87%。 但我仍然只以1.2%的极低概率输掉。 好吧,那条坏的河牌不是那么糟吧?

​ 好吧,还有另外一个因素。 对方在翻牌圈和河牌圈都跟我有大赌注。 他可能比我想像的要好…对吗? 然后,我应该调整我的假定范围。

​ 现在,我认为对方不再拥有77或88的一对,否则,鉴于我的高赌注,他不会跟下去。 我认为他可能有一对9或更好的一对,才能与99、10或QQ配对。 他可能还会有JJ从而导致平局。 或KK和AA,直到转牌时都是头对。 我决定保持10和K或更好的牌,因为有所谓的隐含赔率。 隐含赔率是对您打出的一笔钱可以从投注中赢取多少钱的估计。 因此,对方可能会等待中奖(他可能刚刚中了?)。 因此,我将对方的更新范围定义如下:

villan_range = Range('99+, AT+, KJ+') 
display(HTML(villan_range.to_html()))
print("#combo combinations:" + str(len(villan_range.combos)))

​ 现在,对方的连击数从144降低到了132。让我们计算更新后的赔率。

items = [holdem_calc.calculate_odds_villan(board, exact_calculation, 
                            num_sims, read_from_file , 
                            hero_hand, villan_hand, 
                            verbose, print_elapsed_time = False) for villan_hand in villan_range.combos]

odds = {}
[odds.update({odd_type: np.mean([res[0][odd_type] for res in items if res])}) for odd_type in ["tie", "win", "lose"]]
{'tie': 0.12, 'win': 0.72, 'lose': 0.16}

​ 现在,我有72%的机会获胜(从86%下降的),而我在转牌时的失利几率从3.7%增加到16%。 我决定慎重一下,对方则全押,下注大约70%的彩池。

基本的河牌战略可以告诉您以下内容:

  1. 用你最小的牌作为河牌
  2. 利用您最强的资产押注
  3. 以中等强度的摊牌值检查手牌,以期达到摊牌
for hand_ranking in holdem_functions.hand_rankings:
    print(hand_ranking +": " + str(np.mean([res[1][1][hand_ranking] for res in items if res])))
High Card: 0.0 
Pair: 0.5066666666666667 
Two Pair: 0.08 
Three of a Kind: 0.13333333333333333 
Straight: 0.28 
Flush: 0.0 
Full House: 0.0 
Four of a Kind: 0.0 
Straight Flush: 0.0 
Royal Flush: 0.0

​ 从赔率直方图中,我们可以将对方的可能手牌分为3种类型:

  1. 虚张声势:他拿着{好牌,成对}的几率为60.66%
  2. 中强度牌:他以{0.8}的几率拿着{Two Pair}
  3. 价值下注:他以41.33%的几率持有{三种牌}

​ 对方的全押是有道理的,他持有好牌的概率太低而无法检查。 所以在这里我在想他要么因为虚弱而虚张声势,要么他发疯了,这是一个有价值的选择。 如果您的持牌量最差,那么会虚张声势;如果您的牌很强,则进行价值下注的基本策略有时被称为两极分化下注。 那就是对方在这里所做的。

​ 回顾每种类型的概率(虚张声势,中等强度的手牌,价值下注),我基本上应该至少有60.66%的胜率,这是一个保守的衡量标准,因为对方可能会押注三分之一。 但是我应该跟进吗?

​ 这是另一个称为底池赔率的概念。 底池赔率是指相对于底池大小进行下注的价格。 总而言之,如果我赢得底池的概率大于底池限注价格和底池大小之间的比率,我应该跟注。 让我们做一些数学运算:

  1. 赢取机会≥60.66%(保守估计)
  2. 底池价格= 0.7 *底池大小
  3. 预测底池大小=(1 + 0.7 + 0.7)*底池大小
  4. 底池赔率=底池价格/预测底池大小= 29%

​ 我获胜的机会至少是底池赔率的两倍。 因此,我继续跟进。 结果呢? 对方转过牌。 桌子一度安静,却凝视着桌子上的Ace Jack。

讨论和结论

​ 在本文中,我展示了如何表示基本的扑克元素(例如手牌和组合),以及如何在讲述威尼斯人夜晚的故事的同时,假设Python中的随机手牌和范围来计算扑克赔率。

​ 我们展示了扑克有多么令人兴奋(概率上很有趣)。 在下面,我展示了我的获胜赔率是如何从翻牌到转牌,然后是河牌的改变过程,假设对方的随机牌以及推断范围。

​ 我们观察到,即使最终结果不利于我,我还是赢得这一单挑局的主要人选。 这就是为什么扑克玩家说

您应该专注于做出决定,而不关注所取得的结果。

​ 当然,本文中的所有分析都假设了一些范围和基本的扑克策略,这些策略和基本的扑克策略构成了我在玩游戏时的思维模型,并在本文中以Python实现。 我不是职业扑克玩家,还有很多方法。 我相信我犯了一些错误,例如,低估了对方在翻牌前加注时持有A和J的可能。

​ 我很好奇,其他人将如何使用此处使用的Python框架来分析手牌。

作者:Thársis Souza, PhD

deephub翻译组:孟翔杰

github:https://github.com/souzatharsis/holdem_calc

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服