计算扑克中同花顺的概率

Calculate probability of a flush in poker

我有代码可以继续执行一个循环,直到完成刷新。 现在我正试图在我使用计数来显示处理了多少手然后除以一来获得概率的地方做到这一点。

对于我现在使用的代码,将其计算为 returns 0

from collections import namedtuple
from random import shuffle

Card = namedtuple("Card", "suit, rank")

class Deck:
    suits = '♦♥♠♣'
    ranks = '23456789JQKA'

    def __init__(self):
        self.cards = [Card(suit, rank) for suit in self.suits for rank in self.ranks]
        shuffle(self.cards)

    def deal(self, amount):
        return tuple(self.cards.pop() for _ in range(amount))

flush = False
count = 0
while not flush:

    deck = Deck()
    stop = False
    while len(deck.cards) > 5:
        hand = deck.deal(5)
        # (Card(suit='♣', rank='7'), Card(suit='♠', rank='2'), Card(suit='♥', rank='4'), Card(suit='♥', rank='K'), Card(suit='♣', rank='3'))

        if len(set(card.suit for card in hand)) > 1:
            #print(f"No Flush: {hand}")
            continue
        print(f"Yay, it's a Flush: {hand}")
        flush = True
        break
        if flush:
            break
        else:
            count +=1
print(f'Count is {count}')

顶部还有一些用于初始化方法的代码,如果您也需要,请告诉我

我认为这对你有用。我想这取决于您的 Deck() 是如何定义的。我试图让您的代码保持与您编写它的方式相似的状态,但必须进行一些更改以免出现错误。我实际上也没有 运行 它,因为我没有 Deck 定义。

flush = False
count = 0
while not flush:

    deck = Deck()
    stop = False
    while len(deck.cards) > 5:
        hand = deck.deal(5)
        # (Card(suit='♣', rank='7'), Card(suit='♠', rank='2'), Card(suit='♥', rank='4'), Card(suit='♥', rank='K'), Card(suit='♣', rank='3'))

        if len(set(card.suit for card in hand)) > 1:
            print(f"No Flush: {hand}")
        else:
            print(f"Yay, it's a Flush: {hand}")
            flush = True
            break
        count +=1
print(f'Count is {count}')

但它不会给你获得同花的可能性,老实说,你可能 运行 在几乎每个 运行 获得同花之前 deck ]...

我会考虑将代码更改为这样,以消除一些冗余。

flush = False
count = 0
while not flush:

    deck = Deck()
    while len(deck.cards) > 5:
        hand = deck.deal(5)
        # (Card(suit='♣', rank='7'), Card(suit='♠', rank='2'), Card(suit='♥', rank='4'), Card(suit='♥', rank='K'), Card(suit='♣', rank='3'))

        if len(set(card.suit for card in hand)) == 1:
            print(f"Yay, it's a Flush: {hand}")
            flush = True
            break
        else:
            print(f"No Flush: {hand}")
            count +=1
print(f'Count is {count}')

您的代码(以及@Mason 的答案中可用的代码)将估计最终 使您首先 刷​​新的概率。要估计在 general 中获得同花的概率,我相信这就是您所追求的,您必须 运行 进行数千次实验。在实践中,这称为 Monte Carlo 模拟。

Side note: When I began learning about Monte Carlos I thought they were a sort of "magical", mysteriously complex thing... mostly because their name sounds so exotic. Don't be fooled. "Monte Carlo" is just an overly fancy and arbitrary name for "simulation". They can be quite elementary.

即便如此,模拟还是有点神奇,因为即使很难获得该系统的数学模型,您也可以使用它们从复杂系统中强制解决方案。比方说,您对组合或排列数学没有深刻的理解 - 这会为您的问题提供确切的答案 "What are the odds of getting a flush?"。我们可以 运行 对您的纸牌游戏进行多次模拟,以高度确定地计算出该概率。我已经在下面完成了(注释掉了不需要的原始代码部分):

from collections import namedtuple
from random import shuffle
import pandas as pd

#%% What is the likelyhood of getting flush? Mathematical derivation
""" A flush consists of five cards which are all of the same suit.
We must remember that there are four suits each with a total of 13 cards.
Thus a flush is a combination of five cards from a total of 13 of the same suit.
This is done in C(13, 5) = 1287 ways.
Since there are four different suits, there are a total of 4 x 1287 = 5148 flushes possible. 
Some of these flushes have already been counted as higher ranked hands.
We must subtract the number of straight flushes and royal flushes from 5148 in order to
obtain flushes that are not of a higher rank.
There are 36 straight flushes and 4 royal flushes.
We must make sure not to double count these hands.
This means that there are 5148 – 40 = 5108 flushes that are not of a higher rank. 
We can now calculate the probability of a flush as 5108/2,598,960 = 0.1965%.
This probability is approximately 1/509. So in the long run, one out of every 509 hands is a flush."""
"SOURCE: https://www.thoughtco.com/probability-of-a-flush-3126591"

mathematically_derived_flush_probability = 5108/2598960 * 100

#%% What is the likelyhood of getting flush? Monte Carlo derivation

Card = namedtuple("Card", "suit, rank")

class Deck:
    suits = '♦♥♠♣'
    ranks = '23456789JQKA'

    def __init__(self):
        self.cards = [Card(suit, rank) for suit in self.suits for rank in self.ranks]
        shuffle(self.cards)

    def deal(self, amount):
        return tuple(self.cards.pop() for _ in range(amount))

#flush = False
hand_count = 0
flush_count = 0
flush_cutoff = 150 # Increase this number to run the simulation over more hands.
column_names = ['hand_count', 'flush_count', 'flush_probability', 'estimation_error']
hand_results = pd.DataFrame(columns=column_names)

while flush_count < flush_cutoff:
    deck = Deck()
    while len(deck.cards) > 5:
        hand_count +=1
        hand = deck.deal(5)
        # (Card(suit='♣', rank='7'), Card(suit='♠', rank='2'), Card(suit='♥', rank='4'), Card(suit='♥', rank='K'), Card(suit='♣', rank='3'))
        if len(set(card.suit for card in hand)) == 1:
#            print(f"Yay, it's a Flush: {hand}")
            flush_count +=1
#            break
#        else:
#            print(f"No Flush: {hand}")
        monte_carlo_derived_flush_probability = flush_count / hand_count * 100
        estimation_error = (monte_carlo_derived_flush_probability - mathematically_derived_flush_probability) / mathematically_derived_flush_probability * 100
        hand_df = pd.DataFrame([[hand_count,flush_count,monte_carlo_derived_flush_probability, estimation_error]], columns=column_names)
        hand_results = hand_results.append(hand_df)

#%% Analyze results
# Show how each consecutive hand helps us estimate the flush probability
hand_results.plot.line('hand_count','flush_probability').axhline(y=mathematically_derived_flush_probability,color='r')

# As the number of hands (experiments) increases, our estimation of the actual probability gets better.
# Below the error gets closer to 0 percent as the number of hands increases.
hand_results.plot.line('hand_count','estimation_error').axhline(y=0,color='black')

#%% Memory usage
print("Memory used to store all %s runs: %s megabytes" % (len(hand_results),round(hand_results.memory_usage(index=True,deep=True).sum()/1000000, 1)))

在这种特殊情况下,多亏了数学,我们可以自信地推导出同花的概率为 0.1965%。为了证明我们的模拟得出了正确的答案,我们可以比较它在 80,000 手后的输出:

如您所见,我们模拟的 flush_probability(蓝色)接近数学推导出的概率(黑色)。

同样,下面是模拟概率与数学推导值之间 estimation_error 的图表。如您所见,在模拟的早期 运行 中,估计误差超过 100%,但逐渐上升到误差的 5% 以内。

如果您要 运行 模拟,比如说,手数的两倍,那么我们会看到蓝线和红线最终与两个图表中的黑色水平线重叠 - 表示模拟答案等同于数学推导的答案。

模拟还是不模拟?

最后,你可能会想,

"If I can generate a precise answer to a problem by simulating it, then why bother with all the complicated math in the first place?"

答案是,就像生活中的任何决定一样,"trade offs"。

在我们的示例中,我们可以 运行 对足够多的手进行模拟,以获得高度自信的精确答案。但是,如果因为 不知道 答案(通常是这种情况)而 运行 进行模拟,则需要回答另一个问题,

"How long do I run the simulation to be confident I have the right answer?"

答案似乎很简单:

"Run it for a long time."

最终,您的估计输出可能会收敛到一个值,这样来自其他模拟的输出不会与之前的 运行s 发生显着变化。这里的问题是,在某些情况下,根据您正在模拟的系统的复杂性,看似收敛的输出可能是暂时的现象。也就是说,如果你 运行 再进行十万次模拟,你可能会开始看到你的输出与你认为的稳定答案不同。在不同的情况下,尽管有 运行 数千万次模拟,但可能会出现输出仍未收敛的情况。您有时间编程和 运行 模拟吗?或者数学近似会让你更快到达那里?

还有一个问题:

*"What is the cost?"

今天的消费电脑相对便宜,但在 30 年前,它们的成本 ,000 to ,000 in 2019 dollars. In comparison, a TI89 only cost 5 (again, in 2019 dollars). So if you were asking this question back in 1990 and you were good with probability math, you could have saved ,800 by using a TI89. Cost is just as important today: simulating self-driving cars and protein folding 可能会烧掉数百万美元。

最后,关键任务应用程序可能需要模拟和数学模型来交叉检查这两种方法的结果。一个很好的例子是 StandUpMaths 的 Matt Parker 通过模拟计算 the odds of landing on any property in the game of Monopoly,并用 Hannah Fry 的同一游戏的数学模型证实了这些结果。