这是标准解决方案。它与劳伦斯·贡萨尔维斯的答案类似,但比该答案有两个优点。
- 它是统一的:4 个正整数加起来为 40 的每个组合都有相同的可能性提出这个方案。
and
- 很容易适应其他总计(7 个数字加起来为 100 等)
import random
def constrained_sum_sample_pos(n, total):
"""Return a randomly chosen list of n positive integers summing to total.
Each such list is equally likely to occur."""
dividers = sorted(random.sample(range(1, total), n - 1))
return [a - b for a, b in zip(dividers + [total], [0] + dividers)]
输出示例:
>>> constrained_sum_sample_pos(4, 40)
[4, 4, 25, 7]
>>> constrained_sum_sample_pos(4, 40)
[9, 6, 5, 20]
>>> constrained_sum_sample_pos(4, 40)
[11, 2, 15, 12]
>>> constrained_sum_sample_pos(4, 40)
[24, 8, 3, 5]
说明:(1) 4元组之间存在一一对应关系(a, b, c, d)
正整数使得a + b + c + d == 40
、(2) 整数三元组(e, f, g)
with 0 < e < f < g < 40
,并且很容易使用后者生成random.sample
。对应关系由下式给出(e, f, g) = (a, a + b, a + b + c)
朝一个方向,并且(a, b, c, d) = (e, f - e, g - f, 40 - g)
反方向。
如果你想非负的整数(即允许0
)而不是积极的,那么有一个简单的转换:如果(a, b, c, d)
是非负整数,总和为40
then (a+1, b+1, c+1, d+1)
是正整数,总和为44
,反之亦然。利用这个想法,我们有:
def constrained_sum_sample_nonneg(n, total):
"""Return a randomly chosen list of n nonnegative integers summing to total.
Each such list is equally likely to occur."""
return [x - 1 for x in constrained_sum_sample_pos(n, total + n)]
图形化说明constrained_sum_sample_pos(4, 10)
,感谢@FM。 (稍作编辑。)
0 1 2 3 4 5 6 7 8 9 10 # The universe.
| | # Place fixed dividers at 0, 10.
| | | | | # Add 4 - 1 randomly chosen dividers in [1, 9]
a b c d # Compute the 4 differences: 2 3 4 1