获取n个随机数使它们的总和为m

53

我想获取N个随机数,它们的总和为一个给定的值。

例如,假设我想要5个随机数,它们的总和为1。

那么,一个有效的可能性是:

0.2 0.2 0.2 0.2 0.2

另一个可能性是:

0.8 0.1 0.03 0.03 0.04

等等。我需要这个来创建模糊C均值的所有物矩阵。


可能是随机数加起来等于100:Matlab的重复问题。 - jberryman
1
使用均匀分布?非负数?在范围[0,1]内? - smci
9个回答

64

简短回答:

生成N个随机数,计算它们的总和,将每个随机数除以总和并乘以M。

详细回答:

上述方法不会产生均匀分布,这可能是根据这些随机数用途的问题。Matti Virkkunen提出了另一种方法:

在0到1之间生成N-1个随机数,在列表中添加数字0和1本身,对它们进行排序,并取相邻数字的差。

此处所解释的那样,这会产生均匀分布。


22
然后乘以M(除非M像示例中一样为1)。 - ILMTitan
11
随着N的增加,方差趋近于零,因此这不是一个良好的随机化。 - HAL9000
3
我想跟上“这个解决方案确实提供了分布良好的答案”热潮。 - Ivan
7
这个答案很糟糕。请查看这个回答,它使用美观的图表证明了这个解决方案的错误:https://dev59.com/iWsz5IYBdhLWcg3wNE1q#8068956 - Eddified
1
请查看下面的答案,其中的差异更加准确。 - Harish
显示剩余2条评论

52

生成 N-1 个介于0和1之间的随机数,将数字0和1加入列表中,对列表进行排序,并取相邻数字的差。


1
我不会对我完全不理解的数学问题做出任何保证。 - Matti Virkkunen
1
目前看来,这似乎是唯一一个能产生均匀分布的解决方案(除非我验证时犯了错误,这种情况总是有可能的)。 - Accipitridae
今天,我遇到了同样的问题,发现你的答案非常有帮助。经过一些基本计算,我可以证明所有的 N 个变量都是由相同的概率密度函数 f(x) = M^(1 - N) (-1 + N) (M - x)^(-2 + N) 绘制的。请注意,它的平均值为 M/N,符合预期。 - Sungmin
6
要得到“0和8之间”的结果,请在算法中使用数字8而不是1,并将N设置为3。它的原理类似于取一根长度固定的绳子,在随机位置标记后,然后在标记处剪断。这样你就会得到N段长度不同的绳子,它们加起来等于原始长度。 - Matti Virkkunen
1
如果我有一个数字下限,有没有办法做到这一点? 数字必须大于A。 - Gaurav Fotedar
显示剩余6条评论

27

值得注意的是目前被接受的答案并不能给出均匀分布:

"只需生成N个随机数,计算它们的和,再将每个数除以总和"

让我们看一个当N=2且M=1的简单情况。这是一个微不足道的例子,因为我们可以生成一个列表[x,1-x],其中选择x在范围(0,1)内均匀分布。所提出的解决方案生成一对[x/(x+y),y/(x+y)],其中x和y在(0,1)上均匀分布。为了分析这个问题,我们选择一些z,使得0<z<0.5,并计算第一个元素小于z的概率。如果分布是均匀的,则该概率应为z。然而,我们得到

Prob(x/(x+y) < z) = Prob(x < z(x+y)) = Prob(x(1-z) < zy) = Prob(x < y(z/(1-z))) = z/(2-2z).

我进行了一些快速计算,发现目前唯一能够导致均匀分布的解决方案是由Matti Virkkunen提出的

"生成0到1之间的N-1个随机数,将0和1本身添加到列表中,排序,然后取相邻数字之差。"


1
在你的例子中,x+y = 1,因此P(\frac{x}{x+y} < z) = P(x < z)。你陈述的问题是P(x < y\frac{z}{1-z}) != P(x < y) P(x < \frac{z}{1-z})。如果这是真的,且\frac{z}{1-z} = 10,则P(x < 10y) = P(x < y) P(x < 10) = P(x < y) = 1/2,但实际答案是10/11。 - Apprentice Queue
@实习生队列:请注意,我只分析了上述文本中的0 < z < 0.5情况。你的假设 \frac{z}{1-z} = 10 意味着 z = 10/11。因此,你不能期望这些方程在这种情况下成立。 - Accipitridae
我认为你的分析不正确,因为正常/均匀是指值的分布,在将范围除以常数时不会改变。如果原始分布是均匀的,则除以总和会产生一个均匀分布,其总和为总和。同样适用于正态分布。 - user974465
@John:但你除以的数字取决于所选择的随机值(它不是一个常数),因此它可能会影响分布的均匀性。举个更明显的例子,如果你选择一个均匀随机值 x,然后将其除以 sqrt(x),结果就不是均匀分布的。 - Steve Jessop
1
是的,提供的解决方案并不提供均匀分布。因为您正在对均匀分布应用约束条件,从而改变了分布。因此,虽然 .1 .1 .1 .1 .1 对于原始分布来说是一个很好的生成,但在这个约束条件下,它却不是。因此分布将会改变。 - Carlos Bribiescas
1
我有什么遗漏吗?我知道被接受的答案没有提供一个“正常”的分布,但是它不是提供了一个“均匀”的分布吗?“均匀”不是意味着每个数字都是同样随机的,不会更可能或不可能更高或更低吗?0.2 0.2 0.2 0.2 0.2 加起来等于1。这是一个均匀分布。如果你的目标数字是57而不是1,那么取0.2,除以1,乘以57...然后你得到11.4 11.4 11.4 11.4 11.4,如果我错了,请纠正我,这也是一个均匀分布。人们总是说“显而易见的例子”,但是对我来说,这些例子一点也不明显。 - Eliezer Miron

7

很不幸,这里的一些答案如果您想要均匀随机数是错误的。保证统一随机数最简单(在许多语言中也是最快)的解决方案只需要:

# This is Python, but most languages support the Dirichlet.
import numpy as np
np.random.dirichlet(np.ones(n))*m

其中n是您想要生成的随机数的数量,m是生成数组的总和。这种方法产生正值,并且特别适用于生成总和为1的有效概率(让m = 1)。


1
我发现总和并不总是完美地加起来等于M。 - Alex

4
生成N个正数,使它们随机相加等于正数M,其中每种可能的组合具有相同的概率:
  • Generate N exponentially-distributed random variates. One way to generate such a number can be written as—

      number = -ln(1.0 - RNDU())
    

    where ln(x) is the natural logarithm of x and RNDU() is a method that returns a uniform random variate greater than 0 and less than 1. Note that generating the N variates with a uniform distribution is not ideal because a biased distribution of random variate combinations will result. However, the implementation given above has several problems, such as being ill-conditioned at large values because of the distribution's right-sided tail, especially when the implementation involves floating-point arithmetic. Another implementation is given in another answer.

  • Divide the numbers generated this way by their sum.

  • Multiply each number by M.

结果是N个数字,它们的总和大约等于M(我说“大约”是因为存在舍入误差)。另请参阅维基百科文章狄利克雷分布
这个问题也等同于从N维单位单纯形中均匀生成随机变量的问题。
然而,为了更好的准确性(与实践中经常使用的浮点数相比),您应该考虑生成n个随机整数,这些整数相加得到一个整数m * x,并将这些整数视为分母为xn个有理数的分子(假设m是整数,则它们将总和为m)。您可以选择x为大数,如2的32次方或2的64次方,或其他具有所需精度的数字。如果x为1且m是整数,则解决了生成随机整数总和为m的问题。
以下伪代码展示了两种方法来生成给定正整数和的n个均匀随机整数,以随机顺序排列。(此算法由Smith和Tromble在2004年的“从单位简单形式均匀采样”中提出。)在下面的伪代码中:
- 方法PositiveIntegersWithSum返回n个大于0的整数,它们的总和为m,并以随机顺序排列。 - 方法IntegersWithSum返回n个大于等于0的整数,它们的总和为m,并以随机顺序排列。 - Sort(list)按升序对list中的项进行排序(请注意,排序算法超出了本答案的范围)。

 

METHOD PositiveIntegersWithSum(n, m)
    if n <= 0 or m <=0: return error
    ls = [0]
    ret = NewList()
    while size(ls) < n
      c = RNDINTEXCRANGE(1, m)
      found = false
      for j in 1...size(ls)
        if ls[j] == c
          found = true
          break
        end
      end
      if found == false: AddItem(ls, c)
    end
    Sort(ls)
    AddItem(ls, m)
    for i in 1...size(ls): AddItem(ret,
        ls[i] - ls[i - 1])
    return ret
END METHOD

METHOD IntegersWithSum(n, m)
  if n <= 0 or m <=0: return error
  ret = PositiveIntegersWithSum(n, m + n)
  for i in 0...size(ret): ret[i] = ret[i] - 1
  return ret
END METHOD

在这里,RNDINTEXCRANGE(a, b) 返回一个在区间 [a, b) 中均匀分布的随机整数。

3

在Java中:

private static double[] randSum(int n, double m) {
    Random rand = new Random();
    double randNums[] = new double[n], sum = 0;

    for (int i = 0; i < randNums.length; i++) {
        randNums[i] = rand.nextDouble();
        sum += randNums[i];
    }

    for (int i = 0; i < randNums.length; i++) {
        randNums[i] /= sum * m;
    }

    return randNums;
}

2
然后乘以M(除非M像示例中一样为1)。- ILMTitan Apr 14 at 18:49 - Tobias Kienzler
2
randNums[i] /= sum * m; 相当于 randNums[i] = randNums[i] / (sum * m);。但是需要修改为 randNums[i] = randNums[i] / sum * m;,以确保运算顺序正确。 - Bill the Lizard

3
只需生成N个随机数,计算它们的总和,然后将每一个数除以这个总和。在Guillaume的答案的基础上进行扩展,下面是一个可以实现该功能的Java函数。
public static double[] getRandDistArray(int n, double m)
{
    double randArray[] = new double[n];
    double sum = 0;

    // Generate n random numbers
    for (int i = 0; i < randArray.length; i++)
    {
        randArray[i] = Math.random();
        sum += randArray[i];
    }

    // Normalize sum to m
    for (int i = 0; i < randArray.length; i++)
    {
        randArray[i] /= sum;
        randArray[i] *= m;
    }
    return randArray;
}

在一次测试中,getRandDistArray(5, 1.0) 返回了以下内容:
[0.38106150346121903, 0.18099632814238079, 0.17275044310377025, 0.01732932296660358, 0.24786240232602647]

2
  1. 生成N-1个随机数。
  2. 计算这些随机数的总和。
  3. 将计算出的总和与期望的总和之间的差值添加到集合中。

现在你有N个随机数,它们的总和是期望的总和。


除非你得到最后一个数字是负数。 - Blindy

0

你的限制条件有点少。很多程序都可以工作。

例如,数字是否服从正态分布?均匀分布?
我假设所有数字必须是正数,并且围绕平均值M/N均匀分布。

试试这个。

  1. mean = M/N。
  2. 生成N-1个0到2*mean之间的值。这可以是0到1之间的标准数字u,随机值为(2*u-1)*mean以创建一个适当范围内的值。
  3. 计算N-1个值的总和。
  4. 剩余的值是N-sum。
  5. 如果剩余的值不符合约束条件(0到2*mean),则重复该过程。

“剩余值”并不是均匀选择的,因为(n-1)个均匀随机数之和并不是均匀的。 - Mike Housky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接