【问题标题】:Generating N numbers that sum to 1生成 N 个总和为 1 的数字
【发布时间】:2011-01-11 09:08:05
【问题描述】:

给定一个大小为n 的数组,我想为每个索引生成随机概率,使得Sigma(a[0]..a[n-1])=1

一个可能的结果可能是:

0     1     2     3     4
0.15  0.2   0.18  0.22  0.25

另一个完全合法的结果可能是:

0     1     2     3     4
0.01  0.01  0.96  0.01  0.01

如何轻松快速地生成这些?任何语言的答案都可以,Java 优先。

【问题讨论】:

  • 西格玛是指标准差吗?我希望你意识到,一旦你说标准偏差,你就自动暗示你是从正态分布中抽取随机数。
  • 大多数计算机 RNG 的抽奖号码来自均匀分布。
  • 您可以通过意识到中心极限定理可以提供帮助来绕过这个问题:en.wikipedia.org/wiki/Central_limit_theorem
  • 即你想要从你的正态分布中抽取 1 个数字是从 RNG 均匀分布中抽取 N 个数字并取它们的平均值以产生一个随机的“正态”值,其中 N 足够大以满足您的需求(最好是 N > 1这就是你现在正在做的事情。)
  • @gmatt:不,他的意思是“总和”,看看他的例子

标签: java math random probability


【解决方案1】:

获取 n 个随机数,计算它们的总和,并通过将每个数字除以总和将总和归一化为 1。

【讨论】:

  • 不错 :) 没想到... תודה!
  • 这会引入偏见。您不能以这种方式从单纯形中均匀采样。
  • @dreeves - 你能详细说明一下吗?
  • 本文的图 2 -- cs.cmu.edu/~nasmith/papers/smith+tromble.tr04.pdf -- 直观地描述了您提出的标准化方法的 n=3 情况下的偏差。我相信 kohomologie 的回答是正确的(尽管我没有检查它们包含的代码)。
【解决方案2】:

你试图完成的任务无异于从 N 维单位单纯形中抽取一个随机点。

http://en.wikipedia.org/wiki/Simplex#Random_sampling 可能会对您有所帮助。

一个简单的解决方案可能如下:

public static double[] getArray(int n)
    {
        double a[] = new double[n];
        double s = 0.0d;
        Random random = new Random();
        for (int i = 0; i < n; i++)
        {
           a [i] = 1.0d - random.nextDouble();
           a [i] = -1 * Math.log(a[i]);
           s += a[i];
        }
        for (int i = 0; i < n; i++)
        {
           a [i] /= s;
        }
        return a;
    }

要从 N 维单位单纯形中均匀地画一个点,我们必须取一个指数分布的随机变量向量,然后通过这些变量的总和对其进行归一化.为了得到一个指数分布的值,我们取一个均匀分布的负值log

【讨论】:

  • 在大多数语言中,您应该只创建一次Random,否则您将不会得到随机结果(并且在许多情况下 - 一遍又一遍地重复相同的数字)。我也担心log 的使用 - 你能解释一下为什么会出现吗?
  • +1 供参考,但我认为 nextDouble() 已经针对均匀分布进行了调整:java.sun.com/javase/6/docs/api/java/util/…
  • Kobi,感谢您指出 new Random() 的事情。至于log——我已经编辑了我的帖子以包含更详尽的解释。
  • trashgod,nextDouble() 从 [0,1) 返回一个均匀分布的值。因此,当我们取 n 个这样的值时,我们会在平行面中得到一个随机点。如果我们将该点的坐标除以该点与原点之间的曼哈顿距离,我很不确定我们是否会在单位单纯形中得到一个均匀分布的点。
  • 感谢您的澄清;该算法假定均匀分布并使用 -ln 来获得所需的指数分布。
【解决方案3】:

这相对较晚,但是为了显示对@Kobi 在@dreeves 所指向的paper 中给出的简单直接答案的修改,这使得采样统一。方法(如果我理解清楚的话)是

  1. 从范围 [1, 2, ... , M-1] 中生成 n-1 个 不同 值。
  2. 对结果向量进行排序
  3. 添加 0 和 M 作为结果向量的第一个和最后一个元素。
  4. 通过计算 xi - xi-1 生成一个新向量,其中 i = 1,2, ... n。也就是说,新向量是由旧向量的连续元素之间的差异组成的。
  5. 将新向量的每个元素除以 M。你有你的均匀分布!

我很想知道生成 distinct 随机值并通过除以它们的总和将它们归一化为 1 是否也会产生均匀分布。

【讨论】:

    【解决方案4】:

    如果您想有效地从正态分布中生成值,请尝试Box Muller Transformation

    【讨论】:

      【解决方案5】:

      获取n个随机数,计算它们的总和并将总和归一化为1 将每个数字除以总和。

      Expanding on Kobi's answer,这里有一个 Java 函数可以做到这一点。

      public static double[] getRandDistArray(int n)  {
          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 1
          for (int i = 0; i < randArray.length; i++) {
              randArray[i] /= sum;
          }
          return randArray;
      }
      

      在测试运行中,getRandDistArray(5) 返回以下内容

      [0.1796505603694718, 0.31518724882558813, 0.15226147256596428, 0.30954417535503603, 0.043356542883939767]
      

      【讨论】:

        【解决方案6】:
        public static double[] array(int n){
        
            double[] a = new double[n];
            double flag = 0;
        
            for(int i=0;i<n;i++){
                a[i] = Math.random();
                flag += a[i];
            }
        
            for(int i=0;i<n;i++) a[i] /= flag;
        
            return a;
        }
        

        这里,首先 a 存储随机数。 flag 将保留所有生成的数字的总和,以便在下一个 for 循环中生成的数字将除以 flag,最后数组将具有概率分布中的随机数。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-11-17
          • 2014-09-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多