严格来说,以上将起作用。但是对于 0 到 6 之间的 15 个数字,生成 12 个的几率并没有那么高。事实上,我们可以通过以下方式计算可能性的数量:
F(s, 1) = 1 对于 0≤s≤6
和
F(s, n) = Σ6i=0F(s-i, n-1).
我们可以用一个值来计算:
from functools import lru_cache
@lru_cache()
def f(s, n, mn, mx):
if n < 1:
return 0
if n == 1:
return int(mn <= s <= mx)
else:
if s < mn:
return 0
return sum(f(s-i, n-1, mn, mx) for i in range(mn, mx+1))
这意味着有 9'483'280 种可能性,在 4'747'561'509'943 种可能性中产生 12 的总和,即 0.00019975%。因此,大约需要 500'624 次迭代才能得出这样的解决方案。
因此,我们应该更好地寻找一种直接的方式来生成这样的序列。我们可以通过每次计算生成一个数字的概率来做到这一点:生成 i 作为数字作为 n 个数字序列中的第一个数字的概率,总和为 s 是 F(si, n-1, 0, 6)/F(s, n, 0, 6)。这将保证我们在可能性列表上生成一个 uniform 列表,如果我们每次都绘制一个统一的数字,那么它将不会匹配与给定值匹配的整个值列表上的统一分布条件:
我们可以递归地做到这一点:
from numpy import choice
def sumseq(n, s, mn, mx):
if n > 1:
den = f(s, n, mn, mx)
val, = choice(
range(mn, mx+1),
1,
p=[f(s-i, n-1, mn, mx)/den for i in range(mn, mx+1)]
)
yield val
yield from sumseq(n-1, s-val, mn, mx)
elif n > 0:
yield s
通过上面的函数,我们可以生成numpy数组:
>>> np.array(list(sumseq(15, 12, 0, 6)))
array([0, 0, 0, 0, 0, 4, 0, 3, 0, 1, 0, 0, 1, 2, 1])
>>> np.array(list(sumseq(15, 12, 0, 6)))
array([0, 0, 1, 0, 0, 1, 4, 1, 0, 0, 2, 1, 0, 0, 2])
>>> np.array(list(sumseq(15, 12, 0, 6)))
array([0, 1, 0, 0, 2, 0, 3, 1, 3, 0, 1, 0, 0, 0, 1])
>>> np.array(list(sumseq(15, 12, 0, 6)))
array([5, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1])
>>> np.array(list(sumseq(15, 12, 0, 6)))
array([0, 0, 0, 0, 4, 2, 3, 0, 0, 0, 0, 0, 3, 0, 0])