所以这是交易:我想(例如)生成4个伪随机数,当加在一起时等于40.这怎么可能在python中圆顶?我可以生成一个随机数1-40,然后在1和剩余部分之间生成另一个数字等,但是第一个数字将更有可能“抓住”更多。
答案 0 :(得分:78)
这是标准解决方案。这与Laurence Gonsalves的答案相似,但与答案相比有两个优势。
和
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)
与{ {1}},使用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
是非负整数,则总和为{{1然后(a, b, c, d)
是求积分为40
的正整数,反之亦然。使用这个想法,我们有:
(a+1, b+1, c+1, d+1)
44
的图解说明,感谢@FM。 (稍微编辑。)
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)]
答案 1 :(得分:11)
b = random.randint(2, 38)
a = random.randint(1, b - 1)
c = random.randint(b + 1, 39)
return [a, b - a, c - b, 40 - c]
(我假设你想要整数,因为你说“1-40”,但这可以很容易地为浮点数推广。)
以下是它的工作原理:
答案 2 :(得分:8)
生成4个随机数,计算它们的总和,将每个数除以总和并乘以40。
如果你想要整数,那么这需要一点非随机性。
答案 3 :(得分:4)
from numpy.random import multinomial
multinomial(40, [1/4.] * 4)
答案 4 :(得分:3)
在[1,37]范围内只有37 ^ 4 = 1,874,161个四个整数的排列(允许重复)。枚举它们,保存并计算最多可增加40的排列。 (这将是一个小得多的数字,N)。
在区间[0,N-1]中绘制均匀分布的随机整数K并返回第K个置换。可以很容易地看出这可以保证在可能结果的空间上的均匀分布,每个序列位置相同地分布。 (我看到的许多答案都有最终选择偏低于前三个!)
答案 5 :(得分:1)
假设您希望它们均匀分布,并假设您不想重复
addends = []
picks = range(1, 34)
while sum(addends) != 40:
addends = random.sample(picks, 3)
if sum(addends) > 39:
continue
addends.append(40 - sum(addends))
答案 6 :(得分:0)
通过对除数之间的分布进行控制,以@markdickonson为基础。我介绍了方差/摆动作为它们之间均匀距离的百分比。
def constrained_sum_sample(n, total, variance=50):
"""Return a random-ish list of n positive integers summing to total.
variance: int; percentage of the gap between the uniform spacing to vary the result.
"""
divisor = total/n
jiggle = divisor * variance / 100 / 2
dividers = [int((x+1)*divisor + random.random()*jiggle) for x in range(n-1)]
result = [a - b for a, b in zip(dividers + [total], [0] + dividers)]
return result
样本输出:
[12, 8, 10, 10]
[10, 11, 10, 9]
[11, 9, 11, 9]
[11, 9, 12, 8]
想法仍然是将人口平均分配,然后在给定范围内将其随机向左或向右移动。由于每个值仍然绑定到统一点,因此我们不必担心它会漂移。
对于我的目的来说足够好,但是还不完美。例如:第一个数字将始终较高,而最后一个数字将始终较低。
答案 7 :(得分:0)
如果您想要真正的随机性,请使用:
import numpy as np
def randofsum_unbalanced(s, n):
# Where s = sum (e.g. 40 in your case) and n is the output array length (e.g. 4 in your case)
r = np.random.rand(n)
a = np.array(np.round((r/np.sum(r))*s,0),dtype=int)
while np.sum(a) > s:
a[np.random.choice(n)] -= 1
while np.sum(a) < s:
a[np.random.choice(n)] += 1
return a
如果您想要更高的均匀度,请利用多项式分布:
def randofsum_balanced(s, n):
return np.random.multinomial(s,np.ones(n)/n,size=1)[0]