Python 3.x 当掷N个骰子时,T个总眼睛的概率为S边

Python 3.x 当掷N个骰子时,T个总眼睛的概率为S边,python-3.x,numpy,probability,dice,Python 3.x,Numpy,Probability,Dice,我想计算n骰子与s边(从1到s)的所有眼睛的总和等于t的事件概率。我的语言是Python 3 我目前的方法基本上是一种尝试计数的解决方案,只适用于小数字(运行probability(10,10,50)已经耗尽了我所有的RAM,并迫使我硬重置): 但我真的不知道还能怎么解决这个问题。你能帮我找到正确的路线吗?首先:所有可能的滚动组合将始终是s**n,因此你不需要存储所有可能的列表来获得它的长度。类似地,您可以只保留所需结果的运行总数,而不是保留它们的列表以节省内存空间,但这仍然不会大大加快功能的运

我想计算
n
骰子与
s
边(从1到
s
)的所有眼睛的总和等于
t
的事件概率。我的语言是Python 3

我目前的方法基本上是一种尝试计数的解决方案,只适用于小数字(运行
probability(10,10,50)
已经耗尽了我所有的RAM,并迫使我硬重置):


但我真的不知道还能怎么解决这个问题。你能帮我找到正确的路线吗?

首先:所有可能的滚动组合将始终是
s**n
,因此你不需要存储所有可能的列表来获得它的长度。类似地,您可以只保留所需结果的运行总数,而不是保留它们的列表以节省内存空间,但这仍然不会大大加快功能的运行速度:

def probability(n, s, t):
    all_rolls = itertools.product(range(1,s+1), repeat=n) #no list, leave it a generator
    target_rolls=sum(1 for l in all_rolls if sum(l)==t) #just total them up
    return round(target_rolls/s**n, 4)
一种更有效的计算可能性的方法是使用
dict
和一些巧妙的迭代。每个字典将使用滚动值作为键,频率作为值,每个迭代的
prev
将是前一个X骰子的本指令,
cur
将通过添加另一个骰子从中更新:

import collections
def probability(n, s, t):
    prev = {0:1} #previous roll is 0 for first time
    for _ in range(n):
        cur = collections.defaultdict(int) #current probability
        for r,times in prev.items():
            for i in range(1,s+1):
                #if r occured `times` times in the last iteration then
                #r+i have `times` more possibilities for the current iteration.
                cur[r+i]+=times
        prev = cur #use this for the next iteration

    return cur[t] / s**n
    #return round(cur[t] / s**n , 4)
注1:由于cur是一个默认DICT,因此尝试查找给定输入不可能的数字将返回0


注2:由于此方法将所有可能结果放在一个字典中,因此您可以返回
cur
,并对同一掷骰子上的多个不同可能结果进行计算。

停止制作列表。只需使用惰性评估

from itertools import product

def prob(dice, pips, target):
    rolls = product(range(1, pips+1), repeat=dice)
    targets = sum(1 for roll in rolls if sum(roll) == target)
    return targets / pips**dice
测试:

for i in range(5, 26):
    print(i, prob(5, 5, i))
print('sum: ', sum(prob(5, 5, i) for i in range(5, 26)))
# prints
5 0.00032
6 0.0016
7 0.0048
8 0.0112
9 0.0224
10 0.03872
11 0.0592
12 0.0816
13 0.1024
14 0.1168
15 0.12192  # symmetrical, with peak in middle
16 0.1168
17 0.1024
18 0.0816
19 0.0592
20 0.03872
21 0.0224
22 0.0112
23 0.0048
24 0.0016
25 0.00032
sum:  1.0000000000000002

编辑:已删除未使用的def

itertools。产品速度太慢,无法容纳大于5的大量边和大于6的边。在我的机器上,骰子编号:10和边数:10需要一个半小时来计算。 取而代之的是,我使用这个函数来计算目标,计算时间不到一秒钟

from numpy.polynomial.polynomial import polypow

def probability(dice_number, sides, target):
    """
    Using numpy polynomial
    The number of ways to obtain x as a sum of n s-sided dice
    is given by the coefficients of the polynomial:

    f(x) = (x + x^2 + ... + x^s)^n
    """

    # power series (note that the power series starts from x^1, therefore
    # the first coefficient is zero)
    powers = [0] + [1] * sides
    # f(x) polynomial, computed used polypow in numpy
    poly = polypow(powers, dice_number)
    return poly[target] / sides ** dice_number if target < len(poly) else 0
来自numpy.polynomy.polynomy导入polypow
def概率(骰子数量、侧面、目标):
"""
使用numpy多项式
获得x作为n个s边骰子之和的方法数
由多项式的系数给出:
f(x)=(x+x^2+…+x^s)^n
"""
#幂级数(注意幂级数从x^1开始,因此
#第一个系数为零)
幂=[0]+[1]*边
#f(x)多项式,在numpy中使用polypow计算
多边形=多边形(幂、骰子数)
如果目标
您没有使用
tsum
内部功能?哎呀。我本来打算这样做的,但决定在生成器表达式中使用
if
会更快。移除。prob(8,8,40)花费了大约3秒。我把跑步的概率(10,10,50)留给你。通过公式计算概率比暴力枚举要快得多,但我认为答案不是练习的重点。您的改进很好,因为我现在可以运行大数函数,而不会因为内存溢出而导致机器崩溃,但计算大数仍然需要很长时间。因此我接受了另一个答案。我想你的意思是
t
而不是
x
?@tadhgmdonald Jensen是的,你是对的,我编辑了这个问题来解决这个问题。非常好而且快速的解决方案,尽管我还不完全理解它为什么有效。。。非常感谢。我在完成函数后发现了这个,它在电子表格中演示了相同的过程。可能会帮助你理解它是如何工作的。谢谢,我来看看。
from numpy.polynomial.polynomial import polypow

def probability(dice_number, sides, target):
    """
    Using numpy polynomial
    The number of ways to obtain x as a sum of n s-sided dice
    is given by the coefficients of the polynomial:

    f(x) = (x + x^2 + ... + x^s)^n
    """

    # power series (note that the power series starts from x^1, therefore
    # the first coefficient is zero)
    powers = [0] + [1] * sides
    # f(x) polynomial, computed used polypow in numpy
    poly = polypow(powers, dice_number)
    return poly[target] / sides ** dice_number if target < len(poly) else 0