【问题标题】:Getting N random numbers whose sum is M得到N个总和为M的随机数
【发布时间】:2011-02-08 01:31:18
【问题描述】:

我想得到 N 个随机数,总和是一个值。

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

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

0.2 0.2 0.2 0.2 0.2

另一种可能是:

0.8 0.1 0.03 0.03 0.04

等等。我需要这个来为模糊 C 均值创建一个归属矩阵。

【问题讨论】:

标签: random language-agnostic sum


【解决方案1】:

随机生成 N 个正数,总和为正数 M,其中每个可能的组合都具有相同的可能性:

  • 生成 N 个指数分布的随机变量。生成这样一个数字的一​​种方法可以写成——

      number = -ln(1.0 - RNDU())
    

    其中ln(x)x 的自然对数,RNDU() 是一种返回0 或更大且小于1 的统一随机变量的方法(例如,JavaScript 的Math.random())。请注意,生成具有均匀分布的 N 个数字并不理想,因为会导致随机变量组合的有偏分布。但是,上面给出的实现有几个问题,例如being ill-conditioned at large values,因为分布的右尾,特别是当实现涉及浮点运算时。 another answer 中给出了另一种实现。

  • 用这种方式生成的数字除以它们的总和。

  • 将每个数字乘以 M。

结果是 N 个数字,其总和大约等于 M(由于舍入误差,我说“大约”)。另见维基百科文章Dirichlet distribution

这个问题也等价于generating random variates uniformly from an N-dimensional unit simplex的问题。


但是,为了获得更高的准确性(与使用浮点数的替代方案相比,这在实践中经常发生),您应该考虑生成n random integers that sum to an integer m * x,并将这些整数视为n 有理数的分子和分母x(假设m 是一个整数,则总和为m)。您可以将x 选择为一个较大的数字,例如 232 或 264 或具有所需精度的其他数字。如果x 为0 且m 为整数,这解决了生成随机整数 的问题,其总和为m

下面的伪代码展示了如何生成n 均匀随机整数大于 0 与给定的正和,以随机顺序。 (此算法在 Smith 和 Tromble,“Sampling Uniformly from the Unit Simplex”,2004 中提出。)在下面的伪代码中——

  • PositiveIntegersWithSum 方法以随机顺序返回大于 0 的整数 m
  • IntegersWithSum 方法以随机顺序返回 n 整数 0 或大于 m,并且
  • Sort(list) 按升序对list 中的项目进行排序(请注意,排序算法超出了此答案的范围)。

 

METHOD PositiveIntegersWithSum(n, m)
    if n <= 0 or total <=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, total)
    for i in 1...size(ls): AddItem(ret,
        ls[i] - ls[i - 1])
    return ret
END METHOD

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

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

【讨论】:

    【解决方案2】:

    简答:

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

    更长的答案:

    上述解决方案不会产生均匀分布,这可能是一个问题,具体取决于这些随机数的用途。 Matti Virkkunen 提出的另一种方法:

    在0和1之间生成N-1个随机数,将数字0和1相加 将它们自己添加到列表中,对它们进行排序,然后将它们的差异 相邻的数字。

    这会产生均匀分布,如 here 所述

    【讨论】:

    • 然后乘以 M(除非 M 是 1,如示例中)。
    • 这不是一个好的随机化,因为增加 N 会产生趋于零的方差
    • 我想跳上“这个解决方案确实提供了分布良好的答案”的潮流
    • 这是一个糟糕的答案。请参阅此答案,该答案使用漂亮的图表证明此解决方案不正确:stackoverflow.com/a/8068956/88821
    • 请看下面更准确的取差答案
    【解决方案3】:

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

    # 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)的有效概率特别有用。

    【讨论】:

    • 我发现总和并不总是完美地加起来M,
    【解决方案4】:

    在 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;
    }
    

    【讨论】:

    • > 然后乘以 M(除非 M 像示例中那样为 1)。 – ILMTitan 4 月 14 日 18:49
    • randNums[i] /= sum * m; 等价于randNums[i] = randNums[i] / (sum * m);。这需要是randNums[i] = randNums[i] / sum * m;,以便操作顺序正确。
    【解决方案5】:

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

    Expanding on Guillaume's accepted answer,这里有一个 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]
    

    【讨论】:

      【解决方案6】:

      我认为值得注意的是the currently accepted answer并没有给出均匀分布:

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

      要了解这一点,让我们看一下 N=2 和 M=1 的情况。这是一个简单的情况,因为我们可以通过在 (0,1) 范围内均匀选择 x 来生成列表 [x,1-x]。 建议的解决方案生成一对 [x/(x+y), y/(x+y)],其中 x 和 y 在 (0,1) 中是一致的。为了分析这一点,我们选择一些 z 使得 0

      概率(x/(x+y)

      我做了一些快速计算,似乎到目前为止唯一能导致均匀分布的解决方案是proposed by Matti Virkkunen

      “生成0到1之间的N-1个随机数,将数字0和1本身加入列表,排序,取相邻数字的差。”

      【讨论】:

      • 在你的例子中,x+y = 1 所以 P(\frac{x}{x+y}
      • @Apprentice Queue:请注意,我只是在分析上面文本中 0
      • 我认为您的分析不正确,因为正常/均匀是指值的分布,当将范围除以常数时不会改变。如果原始分布是均匀的,则除以总和会产生一个均匀分布,该分布会加到总和上。正常情况下也是如此。
      • 是的,提供的解决方案不提供均匀分布。因为您将约束应用于改变分布的均匀分布。因此,虽然 .1 .1 .1 .1 .1 对于原始分布来说是一个很好的生成,但在这个约束下,它不是。所以分布会改变。
      • 我错过了什么吗?我知道接受的答案不提供 normal 分布,但它不提供 uniform 分布吗?统一不是意味着每个数字都是同样随机的,并且不会或多或少地更高或更低吗? 0.2 0.2 0.2 0.2 0.2 加起来等于 1。这是一个均匀分布。如果您的目标数字是 57 而不是 1,则取 0.2s,除以 1,乘以 57...您得到 11.4 11.4 11.4 11.4 11.4,如果我错了,请纠正我,这也是均匀分布。人们一直在说“明显的例子”,但没有一个例子对我来说是显而易见的。
      【解决方案7】:
      1. 生成 N-1 个随机数。
      2. 计算所述数字的总和。
      3. 将计算的总和与所需总和之间的差添加到集合中。

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

      【讨论】:

      • 除非你得到最后一个数字是负数。
      【解决方案8】:

      你在约束方面有点苗条。很多很多的程序都会起作用。

      例如,数字是正态分布的吗?制服?
      我假设所有数字都必须是正数并且均匀分布在平均值 M/N 周围。

      试试这个。

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

      【讨论】:

      • “剩余值”不是统一选择的,因为 (n-1) 个统一随机数之和不统一。
      【解决方案9】:

      在0和1之间生成N-1个随机数,将数字0和1本身加入列表,排序,取相邻数字的差。

      【讨论】:

      • 好吧,这太复杂了。如果有人想将其限制为整数,也许有用(显然使用大于 0 到 1 的范围)
      • 我不保证我不完全理解的数学。
      • 看起来这是迄今为止唯一能实现均匀分布的解决方案(除非我在验证时犯了错误,这总是可能的)。
      • @chovy:要获得“8 之间的 0”,请在算法中使用 8 而不是 1,并为 N 使用 3。它起作用的原因是,它就像取一段具有设定长度的字符串,在随机的地方标记它,然后在标记的地方切割它。你最终会得到 N 段字符串,它们必须加起来等于原始长度。
      • 如果我的数字有下限,有没有办法做到这一点?数字必须大于 A。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-09-10
      • 1970-01-01
      • 2017-05-29
      • 2012-04-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多