如何计算n掷m面骰子的总和为某个值或更大的概率
How to calculate the probability of the sum of n rolled m sided dice of being a certain value or greater
我正在尝试编写一些 Python 代码来计算 n 个掷出的 m 个单面公平骰子的总和为某个值或更大值的概率。输入是所有掷骰子的总和、掷骰数和骰子的面数。输出是骰子总和等于或大于该值的百分比机会。
我的计算基于我在这篇论文中找到的方程式,并将其缩放为任何被滚动任意次数的双面骰子:https://digitalscholarship.unlv.edu/cgi/viewcontent.cgi?article=1025&context=grrj
我编写了一些“有效”的代码,但当骰子有很多面时速度非常慢,因此仅对 20 个面或更少面的骰子有用。
import numpy as np
def probability_calculator(roll_total, num_of_rolls, dice_faces):
if num_of_rolls == 0:
probability = 100
elif num_of_rolls == 1:
probability = (dice_faces + 1 - roll_total) * 100/dice_faces
else:
inverse = (dice_faces ** num_of_rolls) ** -1
side_list = np.linspace(1, dice_faces, dice_faces)
expanded_list = np.zeros(dice_faces * num_of_rolls)
stacked_side_list = side_list
for i in range(num_of_rolls - 1):
stacked_side_list = np.vstack((stacked_side_list, side_list))
index_array = np.zeros(num_of_rolls, dtype=int)
while True:
value = 0
for i in range(num_of_rolls):
value = value + stacked_side_list[i][index_array[i]]
expanded_list[int(value) - 1] += 1
if sum(index_array) == (dice_faces - 1) * num_of_rolls:
break
for i in range(num_of_rolls):
if index_array[i] == dice_faces - 1:
index_array[i] = 0
else:
index_array[i] += 1
break
probability = inverse * sum(expanded_list[roll_total - 1:]) * 100
return probability
如您所见,这是非常低效的代码,如果您只掷四个 100 面的骰子,您将不得不迭代 while 循环 100^4 = 100,000,000 次.....
我很确定有一些数学方程式可以简化这段代码并使它 运行 快很多数量级,但数学不是我最喜欢的科目而且我不知道任何方程式或Python 可以提供帮助的功能。
快速浏览了一下论文,被公式吓到了。实施了一种可能不同的方式,简单地计算达到不同总数的频率:
from functools import lru_cache
@lru_cache(None)
def sum_freq(total, rolls, faces):
if not rolls:
return not total
return sum(sum_freq(total - die, rolls - 1, faces)
for die in range(1, faces + 1))
def probability_calculator(roll_total, num_of_rolls, dice_faces):
return sum_freq(roll_total, num_of_rolls, dice_faces) / dice_faces**num_of_rolls
“四个 100 面骰子”演示:
prob_314 = probability_calculator(314, 4, 100)
prob_any = sum(probability_calculator(total, 4, 100)
for total in range(1, 401))
print(f'{prob_314:%}')
print(f'{prob_any:%}')
输出:
0.113564%
100.000000%
十 100 面骰子的输出:
0.050065%
100.000000%
42 个这样的 100 面骰子的输出:
0.000000%
100.000000%
我正在尝试编写一些 Python 代码来计算 n 个掷出的 m 个单面公平骰子的总和为某个值或更大值的概率。输入是所有掷骰子的总和、掷骰数和骰子的面数。输出是骰子总和等于或大于该值的百分比机会。
我的计算基于我在这篇论文中找到的方程式,并将其缩放为任何被滚动任意次数的双面骰子:https://digitalscholarship.unlv.edu/cgi/viewcontent.cgi?article=1025&context=grrj
我编写了一些“有效”的代码,但当骰子有很多面时速度非常慢,因此仅对 20 个面或更少面的骰子有用。
import numpy as np
def probability_calculator(roll_total, num_of_rolls, dice_faces):
if num_of_rolls == 0:
probability = 100
elif num_of_rolls == 1:
probability = (dice_faces + 1 - roll_total) * 100/dice_faces
else:
inverse = (dice_faces ** num_of_rolls) ** -1
side_list = np.linspace(1, dice_faces, dice_faces)
expanded_list = np.zeros(dice_faces * num_of_rolls)
stacked_side_list = side_list
for i in range(num_of_rolls - 1):
stacked_side_list = np.vstack((stacked_side_list, side_list))
index_array = np.zeros(num_of_rolls, dtype=int)
while True:
value = 0
for i in range(num_of_rolls):
value = value + stacked_side_list[i][index_array[i]]
expanded_list[int(value) - 1] += 1
if sum(index_array) == (dice_faces - 1) * num_of_rolls:
break
for i in range(num_of_rolls):
if index_array[i] == dice_faces - 1:
index_array[i] = 0
else:
index_array[i] += 1
break
probability = inverse * sum(expanded_list[roll_total - 1:]) * 100
return probability
如您所见,这是非常低效的代码,如果您只掷四个 100 面的骰子,您将不得不迭代 while 循环 100^4 = 100,000,000 次.....
我很确定有一些数学方程式可以简化这段代码并使它 运行 快很多数量级,但数学不是我最喜欢的科目而且我不知道任何方程式或Python 可以提供帮助的功能。
快速浏览了一下论文,被公式吓到了。实施了一种可能不同的方式,简单地计算达到不同总数的频率:
from functools import lru_cache
@lru_cache(None)
def sum_freq(total, rolls, faces):
if not rolls:
return not total
return sum(sum_freq(total - die, rolls - 1, faces)
for die in range(1, faces + 1))
def probability_calculator(roll_total, num_of_rolls, dice_faces):
return sum_freq(roll_total, num_of_rolls, dice_faces) / dice_faces**num_of_rolls
“四个 100 面骰子”演示:
prob_314 = probability_calculator(314, 4, 100)
prob_any = sum(probability_calculator(total, 4, 100)
for total in range(1, 401))
print(f'{prob_314:%}')
print(f'{prob_any:%}')
输出:
0.113564%
100.000000%
十 100 面骰子的输出:
0.050065%
100.000000%
42 个这样的 100 面骰子的输出:
0.000000%
100.000000%