我正在寻找一种算法或建议来改进我的代码,以生成一个随机数列表,其总和等于某个任意数。使用我的代码,它总是有偏见,因为第一个数字往往会更高。
有没有办法让数字选择更有效率?
#!/usr/bin/python
'''
Generate a list of 'numbs' positive random numbers whose sum = 'limit_sum'
'''
import random
def gen_list(numbs, limit_sum):
my_sum = []
for index in range(0, numbs):
if index == numbs - 1:
my_sum.append(limit_sum - sum(my_sum))
else:
my_sum.append(random.uniform(0, limit_sum - sum(my_sum)))
return my_sum
#test
import pprint
pprint.pprint(gen_list(5, 20))
pprint.pprint(gen_list(10, 200))
pprint.pprint(gen_list(0, 30))
pprint.pprint(gen_list(1, 10))
输出
## output
[0.10845093828525609,
16.324799712999706,
0.08200162072303821,
3.4534885160590041,
0.031259211932997744]
[133.19609626532952,
47.464880208741029,
8.556082341110228,
5.7817325913462323,
4.6342577008233716,
0.22532341156764768,
0.0027495225618908918,
0.064738336208217895,
0.028888697891734455,
0.045250924420116689]
[]
[10]
答案 0 :(得分:12)
为什么不生成正确数量的均匀分布的随机数,将它们加起来并进行缩放?
编辑:要更清楚一点:你想要N个数字加到S?因此,在区间[0,1)或RNG产生的任何内容上生成N个均匀分布的随机数。添加它们,它们将总计s(比如说),而你希望它们总计为S,所以将每个数字乘以S / s。现在我认为数字在[0,S / s]上均匀随机分布。
答案 1 :(得分:9)
我将如何做到这一点:
max
] max
结束,第一个时间间隔将从0开始,并以列表中的第一个数字结束。现在,这些区间的长度总是总计为max
,因为它们只是代表[0,max
]内的段。
代码(在Python中):
#! /usr/bin/env python
import random
def random_numbers(n,sum_to):
values=[0]+[random.randint(0,sum_to) for i in xrange(n-1)]+[sum_to]
values.sort()
intervals=[values[i+1]-values[i] for i in xrange(len(values)-1)]
return intervals
if __name__=='__main__':
print random_numbers(5,100)
答案 2 :(得分:6)
如果您正在寻找具有尽可能少的相关性的正态分布数字,并且需要严格*,我建议您采用以下数学方法并转换为代码。
(*严谨:其他方法的问题在于你的发行版中可以得到“长尾巴” - 换句话说,它很少但可能会有与你预期的输出非常不同的异常值)
每个输出变量的标准差将是(我相信,现在无法验证)sqrt(N / N-1)*输入随机变量的标准差。
**正交矩阵:这是困难的部分,我放入a question at math.stackexchange.com并且有一个简单的矩阵W可以工作,并且可以通过算法定义只有3个不同的值,所以你实际上没有构建矩阵。
W是Vw的Householder反映,其中v = [sqrt(N),0,0,0,...]和w = [1 1 1 1 1 ... 1]可以定义为:
W(1,i) = W(i,1) = 1/sqrt(N)
W(i,i) = 1 - K for i >= 2
W(i,j) = -K for i,j >= 2, i != j
K = 1/sqrt(N)/(sqrt(N)-1)
马克方法的问题:
为什么不生成正确数量的均匀分布的随机数,将它们加起来并进行缩放?
如果你这样做,就会得到一个“长尾”分布。这是MATLAB中的一个例子:
>> X = rand(100000,10);
>> Y = X ./ repmat(sum(X,2),1,10);
>> plot(sort(Y))
我在矩阵X中生成了100,000组N = 10个数,并创建了矩阵Y,其中Y的每一行是X的相应行除以其总和(因此Y的每一行总和为1.0)
绘制Y的排序值(每个列分别排序)产生大致相同的累积分布:
真正的均匀分布将产生从0到最大值的直线。你会注意到它与真正的均匀分布有点类似,除了在有长尾的末端。在0.2和0.5之间产生了过多的数字。对于较大的N值,尾部变得更糟,因为虽然数字的平均值下降(平均值= 1 / N),但最大值保持为1.0:由9个值0.0和1值1.0组成的向量是有效的并且可以通过这种方式生成,但在病理上很少见。
如果您不关心这一点,请继续使用此方法。并且可能有方法生成具有所需总和的“几乎” - 均匀或“几乎” - 高斯分布,这比我上面描述的更简单和更有效。但我提醒你要小心并理解你选择的算法的后果。
一个没有长尾分布均匀分布的修正如下:
MATLAB中N = 10的示例:
>> X = rand(100000,10);
>> Y = X ./ repmat(sum(X,2),1,10);
>> i = sum(X,2)>(10/2)*max(X,[],2);
>> plot(sort(Y(i,:)))
答案 3 :(得分:4)
好的,我们将解决这个问题,假设要求生成一个长度为N的随机向量,该向量在允许的空间上均匀分布,重述如下:
给出
生成长度为N的随机向量V,使得随机变量V在其允许的空间内均匀分布。
我们可以通过注意我们可以计算V = U * S来简化问题,其中U是具有期望总和1的类似随机向量,以及允许值[0,b]的范围,其中b = B / S.值b必须介于1 / N和1之间。
首先考虑N = 3.允许值{U}的空间是垂直于矢量[111]的平面的一部分,该平面穿过点[1/3 1/3 1/3]并且位于多维数据集内,其组件范围介于0和b之间。这组点{U}的形状像六角形。
(TBD:图片。我现在无法生成一个,我需要访问MATLAB或其他可以执行3D绘图的程序。我的Octave安装不能。)
最好使用一个矢量= [1 1 1] / sqrt(3)的正交加权矩阵W(参见我的另一个答案)。一个这样的矩阵是
octave-3.2.3:1> A=1/sqrt(3)
A = 0.57735
octave-3.2.3:2> K=1/sqrt(3)/(sqrt(3)-1)
K = 0.78868
octave-3.2.3:3> W = [A A A; A 1-K -K; A -K 1-K]
W =
0.57735 0.57735 0.57735
0.57735 0.21132 -0.78868
0.57735 -0.78868 0.21132
再次是正交(W * W = I)
如果考虑立方体[0 0 b],[0 bb],[0 b 0],[bb 0],[b 0 0]和[b 0 b]的点,则这些点形成六边形距离立方体的对角线都是b * sqrt(2/3)的距离。这些不能满足问题,但在一分钟内有用。另外两个点[0 0 0]和[b b b]位于立方体的对角线上。
正交加权矩阵W允许我们生成在{U}内均匀分布的点,因为正交矩阵是旋转/反射的坐标变换,不会缩放或倾斜。
我们将生成均匀分布在由W的3个向量定义的坐标系中的点。第一个分量是立方体对角线的轴。 U的分量之和完全取决于该轴,而完全不取决于其他轴。因此,沿该轴的坐标被强制为1 / sqrt(3),这对应于点[1 / 3,1 / 3,1 / 3]。
另外两个组件的方向垂直于立方体的对角线。由于距对角线的最大距离是b * sqrt(2/3),我们将在-b * sqrt(2/3)和+ b * sqrt(2/3)之间生成均匀分布的数字(u,v)。 / p>
这给了我们一个随机变量U'= [1 / sqrt(3)u v]。然后我们计算U = U'* W.一些结果点将超出允许范围(U的每个分量必须在0和b之间),在这种情况下我们拒绝并重新开始。
换句话说:
解决方案类似于更高的尺寸(超平面的一部分内垂直于超立方体主对角线的均匀分布点):
预先计算等级N的加权矩阵W.
范围k(N)是N的函数,其表示边1的超立方体的顶点与其主对角线的最大距离。我不确定通用公式,但是对于N = 5,它是sqrt(2/3),对于N = 5,它是sqrt(6/5),在某个地方可能有一个公式。
答案 4 :(得分:2)
我遇到了这个问题,特别需要整数。答案是使用多项式。
import numpy.random, numpy
total_sum = 20
n = 6
v = numpy.random.multinomial(total_sum, numpy.ones(n)/n)
正如multinomial documentation所解释的那样,你已经推出了一个公平的六面骰子二十次。 v
包含六个数字,表示骰子每一侧出现的次数。自然地,v
的元素必须总和为二十。这里有六个n
,二十个是total_sum
。
使用多项式,您也可以模拟不公平的骰子,这在某些情况下非常有用。
答案 5 :(得分:1)
以下内容非常简单,并返回统一的结果:
def gen_list(numbs, limit_sum):
limits = sorted([random.uniform(0, limit_sum) for _ in xrange(numbs-1)])
limits = [0] + limits + [limit_sum]
return [x1-x0 for (x0, x1) in zip(limits[:-1], limits[1:])]
这个想法很简单,如果你需要比较0到20之间的5个数字,你可以简单地在0到20之间加上4个“限制”,然后得到(0,20)区间的分区。您想要的随机数只是排序列表中的5个区间的长度[0,random1,random2,random3,random4,20]。
PS:哎呀!看起来它和MAK的响应是一样的,尽管没有使用索引编码!答案 6 :(得分:0)
您可以保留一个总计,而不必反复拨打sum(my_sum)
。