生成随机数列表,总和为1

Tom*_*aly 65 python random

我如何列出N(比如100)随机数,以便它们的总和为1?

我可以用随机数列表

r = [ran.random() for i in range(1,100)]
Run Code Online (Sandbox Code Playgroud)

我如何修改它以使列表总和为1(这是用于概率模拟).

seg*_*sai 111

最简单的解决方案确实是采用N个随机值并除以总和.

一个更通用的解决方案是使用Dirichlet发行版 http://en.wikipedia.org/wiki/Dirichlet_distribution ,它可以在numpy中使用.

通过更改分布的参数,您可以更改单个数字的"随机性"

>>> import numpy as np, numpy.random
>>> print np.random.dirichlet(np.ones(10),size=1)
[[ 0.01779975  0.14165316  0.01029262  0.168136    0.03061161  0.09046587
   0.19987289  0.13398581  0.03119906  0.17598322]]

>>> print np.random.dirichlet(np.ones(10)/1000.,size=1)
[[  2.63435230e-115   4.31961290e-209   1.41369771e-212   1.42417285e-188
    0.00000000e+000   5.79841280e-143   0.00000000e+000   9.85329725e-005
    9.99901467e-001   8.37460207e-246]]

>>> print np.random.dirichlet(np.ones(10)*1000.,size=1)
[[ 0.09967689  0.10151585  0.10077575  0.09875282  0.09935606  0.10093678
   0.09517132  0.09891358  0.10206595  0.10283501]]
Run Code Online (Sandbox Code Playgroud)

根据主要参数,Dirichlet分布将给出所有值接近1./N的向量,其中N是向量的长度,或者给出向量,其中向量的大部分值将为〜0,并且将是单一的1,或在这些可能性之间给予一些东西.

编辑(原答案后5年):关于Dirichlet分布的另一个有用的事实是,如果你生成一组Gamma分布的随机变量,然后用它们的总和除以它,你自然会得到它.

  • +1是唯一一个提到Dirichlet分布的人.这应该是答案. (3认同)
  • 我已将我接受的答案更改为这个答案,因为缩放不一定会给出统一的分布。 (2认同)
  • @Tom,我不会嫉妒你的选择,这个答案很好,但我想澄清一些事情:缩放_确实_一定会给出均匀分布(在“[0,1/s)”之上)。它将与您开始使用的未缩放分布完全相同,因为缩放不会改变分布,而只是压缩它。这个答案给出了多种分布,其中只有一种是均匀的。如果这对您来说没有意义,请运行示例并查看一些直方图以使其清楚。还可以尝试使用高斯分布(`np.random.normal`)进行同样的操作。 (2认同)

ask*_*han 33

执行此操作的最佳方法是简单地列出所需数量的数字,然后将它们全部除以总和.他们这种方式完全随机.

r = [ran.random() for i in range(1,100)]
s = sum(r)
r = [ i/s for i in r ]
Run Code Online (Sandbox Code Playgroud)

或者,正如@TomKealy所建议的那样,将总和和创造保持在一个循环中:

rs = []
s = 0
for i in range(100):
    r = ran.random()
    s += r
    rs.append(r)
Run Code Online (Sandbox Code Playgroud)

要获得最快的性能,请使用numpy:

import numpy as np
a = np.random.random(100)
a /= a.sum()
Run Code Online (Sandbox Code Playgroud)

您可以为随机数提供您想要的任何分布,以获得概率分布:

a = np.random.normal(size=100)
a /= a.sum()
Run Code Online (Sandbox Code Playgroud)

----时间----

In [52]: %%timeit
    ...: r = [ran.random() for i in range(1,100)]
    ...: s = sum(r)
    ...: r = [ i/s for i in r ]
   ....: 
1000 loops, best of 3: 231 µs per loop

In [53]: %%timeit
   ....: rs = []
   ....: s = 0
   ....: for i in range(100):
   ....:     r = ran.random()
   ....:     s += r
   ....:     rs.append(r)
   ....: 
10000 loops, best of 3: 39.9 µs per loop

In [54]: %%timeit
   ....: a = np.random.random(100)
   ....: a /= a.sum()
   ....: 
10000 loops, best of 3: 21.8 µs per loop
Run Code Online (Sandbox Code Playgroud)

  • 我觉得是时候喝啤酒了. (3认同)
  • @Tom不用担心,很容易陷入困境,试图让这些东西比他们更难:)现在它就在这里为下一个人. (2认同)
  • 缩放不一定是好的.请参阅我的回答.从[0,1)^ n到目标空间(x_i = 1的总和)有许多可能的映射,并且它们不能都是均匀的! (2认同)

Mik*_*sky 7

将每个数字除以总数可能无法为您提供所需的分配.例如,对于两个数字,对x,y = random.random(),random.random()在平方0 <= x <1,0 <= y <1上均匀地选取一个点.将(x,y)点的"项目"除以沿(x,y)到原点的直线x + y = 1的和.接近(0.5,0.5)的点将比接近(0.1,0.9)的点更可能.

对于两个变量,则x = random.random(),y = 1-x给出沿几何线段的均匀分布.

使用3个变量,您将在立方体中拾取随机点并沿径向(通过原点)投影,但三角形中心附近的点将比顶点附近的点更可能.结果点位于x + y + z平面的三角形上.如果您需要在该三角形中无偏差地选择点,则缩放并不好.

这个问题在n维中变得复杂,但你可以通过从非负整数的所有n元组的集合中均匀地选择来获得低精度(但是对于所有实验科学粉丝来说都是高精度!) N,然后将它们各自除以N.

我最近想出了一个算法来为中等大小的n,N做这个.它应该适用于n = 100和N = 1,000,000来为你提供6位数的随机数.请参阅我的回答:

创建受约束的随机数?


pjs*_*pjs 6

创建一个由0和1组成的列表,然后添加99个随机数.对列表进行排序.连续的差异将是加起来为1的区间长度.

我不熟悉Python,所以如果有更多的Pythonic方法,请原谅我.我希望意图很明确:

import random

values = [0.0, 1.0]
for i in range(99):
    values.append(random.random())
values.sort()
results = []
for i in range(1,101):
    results.append(values[i] - values[i-1])
print results
Run Code Online (Sandbox Code Playgroud)

这是Python 3中的更新实现:

import random

def sum_to_one(n):
    values = [0.0, 1.0] + [random.random() for _ in range(n - 1)]
    values.sort()
    return [values[i+1] - values[i] for i in range(n)]

print(sum_to_one(100))
Run Code Online (Sandbox Code Playgroud)


小智 5

除了@pjs 的解决方案,我们还可以定义一个带有两个参数的函数。

import numpy as np

def sum_to_x(n, x):
    values = [0.0, x] + list(np.random.uniform(low=0.0,high=x,size=n-1))
    values.sort()
    return [values[i+1] - values[i] for i in range(n)]

sum_to_x(10, 0.6)
Out: 
[0.079058655684546,
 0.04168649034779022,
 0.09897491411670578,
 0.065152293196646,
 0.000544800901222664,
 0.12329662037166766,
 0.09562168167787738,
 0.01641359261155284,
 0.058273232428072474,
 0.020977718663918954]  
Run Code Online (Sandbox Code Playgroud)