生成N个均匀随机数,总和为M.

时间:2015-06-05 05:27:42

标签: python algorithm random distribution

之前已经问过这个问题,但我从来没有真正看到过这么好的答案。

  1. 我想生成8个总和为0.5的随机数。

  2. 我希望从统一分布中随机选择每个数字(即,下面的简单函数不起作用,因为数字不会均匀分布)。

    -parameters
  3. 代码应该是可推广的,这样你就可以生成N个均匀随机数,它们总和为M(其中M是正浮点数)。如果可能的话,您还可以解释(或用图表显示)为什么您的解决方案在适当的范围内统一生成随机数?

    错过商标的相关问题:

    Generate multiple random numbers to equal a value in python(当前接受的答案是不均匀的 - 另一个统一的答案只适用于整数)

    Getting N random numbers that the sum is M(同样的问题在Java中,目前接受的答案是完全错误的,也没有统一分布的答案)

    Generate N random integers that sum to M in R(同样的问题,但在R中,正常 - 不均匀 - 分布)

    非常感谢任何帮助。

5 个答案:

答案 0 :(得分:4)

你所要求的似乎是不可能的。

但是,我会重新解释您的问题,以便更有意义并且可以解决。您需要的是七维超平面x_1 + x_2 + ... + x_8 = 0.5上的概率分布。由于超平面的范围是无限的,因此整个超平面上的均匀分布将不起作用。您可能(?)想要的是所有x_i>0的超平面块。该区域是单形,三角形的一般化,单形上的均匀分布是Dirichlet分布的一个特例。

你可能会发现Dirichlet发布维基百科文章string cutting的这一部分,特别有启发性。

<强>实施

维基百科文章在Random Number Generation部分的Python中提供了以下实现:

params = [a1, a2, ..., ak]
sample = [random.gammavariate(a,1) for a in params]
sample = [v/sum(sample) for v in sample]

您可能(?)想要的是所有ai=1导致单纯形均匀分布的情况。此处k对应于您问题中的N个数字。要将样本汇总到M而不是1,只需将sample乘以M

<强>更新

感谢Severin Pappadeux指出,在极少数情况下,伽玛变量可以返回无穷大。这在数学上是不可能的&#34;但是可以作为浮点数方面的实现工件出现。我处理这种情况的建议是在首次计算sample之后检查它;如果sample的任何组件都是无穷大,则将所有非无限分量设置为0并将所有无穷大分量设置为1.然后,当计算xi时,结果如xi=1, all other x's=0 ,或xi=1/2, xj=1/2, all other x's=0将导致集体&#34;角落样本&#34;和#34;边缘样本&#34;。

另一个非常低概率的可能性是伽马变换的总和溢出。我猜我们可以通过整个底层伪随机数序列而不是看到这种情况发生,但理论上它是可能的(取决于底层的伪随机数发生器)。可以通过重新调整sample来处理这种情况,例如,在计算了伽马变换之后但在计算x之前,将sample的所有元素除以N。就个人而言,我不会因为赔率太低而烦恼;程序崩溃由于其他原因会有更高的概率。

答案 1 :(得分:2)

我们可以从“0-M”范围内的均匀分布中选择“n-1”随机区间,而不是从均匀分布中选择“n”个数,然后我们可以提取区间。 / p>

from random import uniform as rand

def randConstrained(n, M):
     splits = [0] + [rand(0, 1) for _ in range(0,n-1)] + [1]
     splits.sort()
     diffs = [x - splits[i - 1] for i, x in enumerate(splits)][1:]
     result = map(lambda x:x*M, diffs)
     return result

res = randConstrained(8,0.5)
print res
print sum(res)

输出

[0.0004411388173262698,
 0.014832306834761111,
 0.009695872790939863,
 0.04539251424140245,
 0.18791325446494067,
 0.07615024971405443,
 0.07792489571128014,
 0.08764976742529507]

0.5

答案 2 :(得分:0)

对于一个完全通用的解决方案(&#34;我想要nlow之间的high个数字,该总和为m):

from random import uniform as rand

def randConstrained(n, m, low, high):
    tot = m
    if not low <= 0 <= high:
        raise ValueError("Cannot guarantee a solution when the input does not allow for 0s")
    answer = []
    for _ in range(n-1):
        answer.append(low + rand(0,tot) * (high-low))
        tot -= answer[-1]
    answer.append(m-sum(answer))
    return answer

对于您的情况,可以按如下方式使用:

In [35]: nums = randConstrained(8, 0.5, 0, 1)

In [36]: nums
Out[36]: 
[0.2502590281277123,
 0.082663797709837,
 0.14586995648173873,
 0.011270073049224807,
 0.009328970756471237,
 0.00021993111786291258,
 0.0001831479074098452,
 0.000205094849743237]

答案 3 :(得分:0)

它被称为单纯形采样,它与Dirichlet分布密切相关。 Sum(x_i)= 1,其中每个x_i是U(0,1)。在你的情况下,在单纯采样之后,只需将每个x_i乘以0.5。

无论如何,将c ++代码从https://github.com/Iwan-Zotow/SimplexSampling转换为python(希望,没有太多错误)

它处理无限正确

"text ..."

答案 4 :(得分:0)

与k4vin的解决方案相同,但没有导入错误,而我是随机的。均匀。

import random

def rand_constrained(n, total):
    # l is a sorted list of n-1 random numbers between 0 and total
    l = sorted([0] + [total * random.random() for i in range(n - 1)] + [total])
    # Return the intervals between each successive element
    return [l[i + 1] - l[i] for i in range(n)]

print(rand_constrained(3, 10))
# [0.33022261483938276, 8.646666440311822, 1.0231109448487956]

但是matplotlib在安装时很窒息,所以我现在无法将其绘制出来。