以上内容在严格意义上是可以运行的。但对于0到6之间的15个数字,生成12的概率并不那么高。实际上,我们可以通过以下公式来计算可能性的数量:
当 0≤s≤6 时,
F(s, 1) = 1
并且
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))
这意味着在总计 4'747'561'509'943 种可能性中,有 9'483'280 种可能性生成总和为 12,占比 0.00019975%。因此,需要大约 500'624 次迭代才能得出这样的解决方案。
因此,我们最好找到一种简单直接的方法来生成这样的序列。我们可以通过每次计算生成数字的概率来实现:作为前 n 个数字之和为 s 的序列的第一个数字生成 i 的概率是 F(s-i, n-1, 0, 6)/F(s, n, 0, 6)。如果我们每次抽取一个均匀分布的数字,则这将保证我们在所有符合给定条件的值列表上生成均匀的列表,而不是与整个列表的均匀分布相匹配。
我们可以使用递归来实现此操作:
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])