比较扩展牌组大小的总和和增加牌组中每次重复的抽奖(使用 python 3.10)

问题描述 投票:0回答:1

规则是这样的

  1. 底牌是[2,3,4,5,6,7,8,9,10],我们可以称之为牌号1
  2. 每增加一次 Deck Size,Deck 就会扩展以包含一个复制副本。
  3. 每层甲板在甲板的计算尺寸之后延伸 [7,8,9,10]
  4. 甲板尺寸 2 将是 [2,3,4,5,6,7,8,9,10,2,3,4,5,6,7,8,9,10,7,8,9,10] 和尺寸 3 [2,3,4,5,6,7,8,9,10,2,3,4,5,6,7,8,9,10,2,3,4,5,6,7,8 ,9,10,7,8,9,10] 等等。
  5. 例如,牌组大小为 3,游戏结果将是平局的总和,其中平局数等于牌组大小。甲板尺寸 3 将是 sum(draw1, draw2,draw3)

我相信这会给出正确的甲板尺寸

import itertools

# define base deck
base_deck = [2, 3, 4, 5, 6, 7, 8, 9, 10]

# define deck sizes
deck_a_size = 2
deck_b_size = 3

# create decks
deck_a_cards = list(itertools.chain.from_iterable(itertools.repeat(base_deck, deck_a_size)))
deck_a_cards.extend(floater)
print(deck_a_cards)
deck_b_cards = list(itertools.chain.from_iterable(itertools.repeat(base_deck, deck_b_size)))
deck_a_cards.extend(floater)
print(deck_b_cards)

但是,我不确定这是否会计算出正确的概率

对于和a,b

import itertools

# define base deck
base_deck = [2, 3, 4, 5, 6, 7, 8, 9, 10]

# define deck sizes
deck_a_size = 1
deck_b_size = 3

# create decks
deck_a_cards = list(itertools.chain.from_iterable(itertools.repeat(base_deck, deck_a_size)))
deck_a_cards.extend(floater)
print(deck_a_cards)
deck_b_cards = list(itertools.chain.from_iterable(itertools.repeat(base_deck, deck_b_size)))
deck_b_cards.extend(floater)
print(deck_b_cards)

deck_a_success = 0
deck_b_success = 0
total_combinations = 0

for i in itertools.product(deck_a_cards, repeat=deck_a_size):
    for j in itertools.product(deck_b_cards, repeat=deck_b_size):
        deck_a_sum = sum(i)
        deck_b_sum = sum(j)
        if deck_a_sum > deck_b_sum:
            deck_a_success += 1
        elif deck_a_sum < deck_b_sum:
            deck_b_success += 1
        else:
            deck_b_success += 1
        total_combinations += 1

deck_a_prob = deck_a_success / total_combinations
deck_b_prob = deck_b_success / total_combinations

print("Probability of success with {} cards drawn:".format(deck_a_size + deck_b_size))
print("Deck A:", deck_a_prob)
print("Deck B:", deck_b_prob)

最后必须有更好的方法来做到这一点,因为这是非常糟糕的时间复杂度。

好吧,我现在也尝试过蒙特卡洛模拟

对于尺寸 2,3 和 1,3 我得到非常相似的值:

import random

base_deck = [2, 3, 4, 5, 6, 7, 8, 9, 10]
deck_a_size = 2
deck_b_size = 3

num_trials = 1000000
deck_a_wins = 0
deck_b_wins = 0

for i in range(num_trials):
    deck_a_sum = sum(random.sample(base_deck, deck_a_size))
    deck_b_sum = sum(random.sample(base_deck * deck_b_size, deck_b_size))
    if deck_a_sum > deck_b_sum:
        deck_a_wins += 1
    elif deck_b_sum > deck_a_sum:
        deck_b_wins += 1

deck_a_prob = deck_a_wins / num_trials
deck_b_prob = deck_b_wins / num_trials

print("Probability of success with {} cards drawn:".format(deck_a_size+deck_b_size))
print("Deck A:", deck_a_prob)
print("Deck B:", deck_b_prob)

抽到5张牌的成功概率: 甲板A:0.1316926655146557 B 层:0.8683073344853444 运行时间 6.0s

与蒙特卡洛

抽到5张牌的成功概率: 甲板A:0.121357 甲板 B:0.837629 运行时间 3.8s

python nested nested-loops probability repeat
1个回答
0
投票

你的结果似乎是正确的。

由于您的代码使用的是产品(而不是排列),我假设进行多次抽奖时,抽出的牌会在下一次抽奖之前放回牌组中。这允许通过为给定牌组大小的每个可能总和构建概率列表来简化过程。

基于这些概率,可以将一副牌中的每个可能总和与另一副牌中的总和进行比较,并产生一个结果概率,然后我们可以将其相加得到最终结果:

from itertools import product

floaters = [7,8,9,10]
baseDeck = [2, 3, 4, 5, 6, 7, 8, 9, 10]
Bs   = len(baseDeck)
Fs   = len(floaters)

def getSumProb(size):
    prob = {n:(size+(n in floaters))/(size*Bs+Fs) for n in baseDeck}
    sump = prob
    for _ in range(1,size):
        sump,prev = dict(),sump
        for (s,ps),(n,pn) in product(prev.items(),prob.items()):
            sump[s+n] = sump.get(s+n,0) + ps * pn
    return sump

getSumProb
函数返回一个字典,其中可能的总和作为键,它们对应的概率作为值。

它首先计算每个单独数字的概率,同时考虑到浮动值的重复和增加的机会。然后,它将这个单独概率列表“折叠”到自身上以获得总和值(对于多次抽奖)及其各自的概率(即形成最终总和的概率乘积之和)。

为了计算 deckA 抽奖胜过 deckB 的概率,我们只需将 deckA 抽奖的每个可能总和的概率乘以 deckB 总和较低的概率总和。

输出:

sumsA  = getSumProb(2)
sumsB  = getSumProb(3)

winAprob = sum( pA*sum(pB for sB,pB in sumsB.items() if sA>sB) 
                for sA,pA in sumsA.items() )

winBprob = 1 - winAprob

print(winAprob) # 0.13169266551465567
print(winBprob) # 0.8683073344853444

# runs in 0.0002 sec. on my laptop

如果您需要隔离两个值相等的情况,您可以分别计算相等和的概率并从中推导出 winBprob

equalProb = sum( pA*sum(pB for sB,pB in sumsB.items() if sA==sB) 
                 for sA,pA in sumsA.items() )

winBprob = 1 - winAprob - equalProb

print(equalProb)  # 0.04061934507371048
print(winBprob)   # 0.8276879894116339
© www.soinside.com 2019 - 2024. All rights reserved.