RPG骰子高数值场景下概率计算代码异常问题排查
骰子投掷成功概率计算函数bug排查
问题背景
我编写了calc_dados函数用于计算骰子投掷的成功概率,小数值场景下运行正常,但大数值场景下结果异常。通过pandas生成模拟硬币(2面骰子)的概率表时,发现结果与暴力测试的正确结果不符——例如4枚硬币至少出现1次正面的正确概率为93.75%,但函数输出为81.25%,请求协助排查问题原因。
原函数代码:
def calc_dados(f_sucessos = 1, faces = 6, n_dados = 1): p_max = ((f_sucessos/faces)**n_dados) #chance de todos fator = 1 p_meio = 0 for i in range(n_dados-1): p_meio += (((f_sucessos/faces)**(n_dados-fator) * ((faces-f_sucessos)/faces)**(n_dados-(n_dados-fator))) * n_dados) fator += 1 p = p_max + p_meio return p*100
测试代码:
import pandas as pd f_sucessos = 1 # 成功面数 faces = 2 # 骰子总面数 n_dados = 10 # 投掷骰子数量 suc_list = [] for i in range(0,n_dados): suc_list.append(f_sucessos) fac_list = [] for i in range(0,n_dados): fac_list.append(faces) cha_list = [] for i in range(0,n_dados): cha_list.append(calc_dados(f_sucessos, faces, i+1)) df = pd.DataFrame( { "n_dados" : range(1,n_dados+1), "faces" : fac_list, "sucessos" : suc_list, "chance" : cha_list } ) df
问题原因
函数核心逻辑存在两处关键错误:
- 概率项构造错误:循环中成功/失败次数的对应关系混乱,
((faces-f_sucessos)/faces)**(n_dados-(n_dados-fator))简化后是失败次数为fator、成功次数为n_dados-fator,这和「至少1次成功」需要覆盖的1到n次成功的情况完全不匹配。 - 组合数错误:直接用
n_dados作为每个概率项的系数,但实际上每种「k次成功、n-k次失败」的情况对应的组合数是从n个骰子中选k个的组合数C(n,k),而非固定的n。
以n=4为例,原函数计算的是「4次成功」+「3次成功1次失败」*4 +「2次成功2次失败」*4,完全遗漏了「1次成功3次失败」的情况,结果自然偏差。
修正方案
方案1:补集法(推荐,高效不易错)
计算「至少1次成功」的概率,最简便的方法是用1减去「全失败」的概率:
def calc_dados(f_sucessos=1, faces=6, n_dados=1): # 单次骰子失败的概率 p_single_fail = (faces - f_sucessos) / faces # 所有骰子都失败的概率 p_all_fail = p_single_fail ** n_dados # 至少1次成功的概率 = 1 - 全失败概率 p_success = 1 - p_all_fail return p_success * 100
方案2:修正原逻辑(直接枚举所有成功情况)
如果坚持要用原思路枚举所有成功情况,需要引入组合数计算(Python 3.10+支持math.comb):
import math def calc_dados(f_sucessos=1, faces=6, n_dados=1): p_single_success = f_sucessos / faces p_single_fail = 1 - p_single_success total_prob = 0 # 遍历1次成功到n_dados次成功的所有情况 for k in range(1, n_dados + 1): # 组合数:从n个骰子中选k个成功的情况数 comb_count = math.comb(n_dados, k) # 当前情况的概率:组合数 * 成功概率^k * 失败概率^(n-k) case_prob = comb_count * (p_single_success ** k) * (p_single_fail ** (n_dados - k)) total_prob += case_prob return total_prob * 100
验证结果
用n=4测试,两种修正后的函数都会返回93.75%,符合正确结果;测试n=10时,也能得到正确的「至少1次正面」概率99.90234375%。
内容的提问来源于stack exchange,提问作者Daniel Capua
相关产品推荐
相关产品推荐

