如何更快地迭代十个列表(每个十个元素)的笛卡尔积? (概率与骰子)

How to iterate through the Cartesian product of ten lists (ten elements each) faster? (Probability and Dice)

我正在尝试解决 this task。 我为此写了 function,它使用 itertools.product() 作为输入迭代的笛卡尔积:

def probability(dice_number, sides, target):
    from itertools import product
    from decimal import Decimal
    FOUR_PLACES = Decimal('0.0001')
    total_number_of_experiment_outcomes = sides ** dice_number
    target_hits = 0
    sides_combinations = product(range(1, sides+1), repeat=dice_number)
    for side_combination in sides_combinations:
        if sum(side_combination) == target:
            target_hits += 1
    p = Decimal(str(target_hits / total_number_of_experiment_outcomes)).quantize(FOUR_PLACES)
    return float(p)

当调用 probability(2, 6, 3) 时输出是 0.0556,所以工作正常。 但是调用 probability(10, 10, 50) 计算时间很长(几个小时?),但必须有更好的方法:)
for side_combination in sides_combinations: 需要很长时间才能遍历大量 sides_combinations.

拜托,你能帮我看看如何加快结果的计算吗,我今晚也想睡觉..

我猜想问题是求骰子总和的分布。一种有效的方法是通过离散卷积。变量总和的分布是它们的概率质量函数(或密度,在连续情况下)的卷积。卷积是一个 n 元运算符,因此您可以方便地一次只计算两个 pmf(目前的总数分布,以及列表中的下一个)。然后从最终结果中,您可以读出每个可能总数的概率。结果中的第一个元素是最小可能总数的概率,最后一个元素是最大可能总数的概率。在这两者之间,您可以找出哪一个对应于您要查找的特定金额。

这个最难的部分是卷积,所以先处理它。这只是一个简单的求和,只是要正确求和的极限有点棘手。我的建议是使用整数或有理数,这样你就可以进行精确的算术运算。

之后你只需要为每个输入芯片构造一个合适的pmf。输入只是 [1, 1, 1, ... 1] 如果你使用整数(你最终必须归一化)或 [1/n, 1/n, 1/n, ..., 1 /n] 如果有理数,其中 n = 面数。此外,您还需要正确标记输出的索引——同样,要做到这一点有点棘手。

卷积是一种非常通用的变量求和方法。由于 FFT(conv(A, B)) = FFT(A) FFT(B),因此通过快速傅里叶变换实现卷积可以使其更加高效。但在这一点上,我认为你不需要担心这个。

如果有人仍然对通过所有 itertools.product 笛卡尔积避免非常非常非常长的迭代过程的解决方案感兴趣,这里是:

def probability(dice_number, sides, target):
    if dice_number == 1:
        return (1 <= target <= sides**dice_number) / sides
    return sum([probability(dice_number-1, sides, target-x) \
                        for x in range(1,sides+1)]) / sides

但是你应该添加 probability 函数结果的缓存,如果你不这样做的话——概率的计算也会花费非常非常非常长的时间)

P.S。这个代码 100% 不是我的,我是从网上拿来的,我不是很聪明自己制作它,希望你会像我一样喜欢它。