【问题标题】:How to generate three random numbers, whose sum is 1?如何生成三个和为1的随机数?
【发布时间】:2011-07-30 15:30:29
【问题描述】:

我需要生成3个随机数,数量等于1。

我的实现不支持统一分布。 :(

【问题讨论】:

  • 您自己尝试过吗?问题是什么?你能发布一些代码吗?
  • 这三个数字不能从均匀分布中得出,因为这意味着样本之间的独立性,而您对值有明确的限制 (x+y+z=1)。
  • @Ian Ringrose:不,个人项目。
  • 这只是两个随机数
  • (Simen S) 的答案不正确,超过了中心的样本,见MATHWORLD

标签: c# random distribution uniform


【解决方案1】:

只需获取 3 个随机数,然后计算一个因子,即 1 / [您的数字之和]。最后将每个随机数乘以该因子。总和为 1。

【讨论】:

  • 这是迄今为止发布的 3 种方法中最明显的统一;达人的可能是统一的,但我不得不考虑;这个是显然统一的。但是,您可能会遇到舍入问题
  • @Simen S 非常感谢老兄......你让数学变得如此简单
【解决方案2】:

这实际上是一个棘手的问题。首先:
达人的解法并不统一,因为它不支持两个大于 1/3 的数。
Simen 的解决方案并不统一,假设“选择随机数”来自均匀分布,但这有点微妙。它至少在变量之间是对称的(即 [a, b, c] 的概率与它的任何排列的概率相同),但它非常有利于更接近 (1/3, 1/3, 1/ 3)。通过查看极端情况来这样想: (1/3, 1/3, 1/3) 可能来自任何 (a, a, a),其中 a 的范围从 0 到 1。 (1, 0, 0),一个同样有效的三元组,必须来自 (1, 0, 0)。

一种解法: 加1的一组正数组成一个三空间的等边三角形,坐标为(1,0,0), (0,1,0), ( 0,0,1)。将其扩展到平行四边形——例如通过添加一个点 (1,1,-1) 作为第四个点。这个 double 是区域 - 将第二个区域映射到第一个区域,以便在这个平行四边形中选择一个随机点就足够了。

平行四边形可以通过 (0,0,1) + A(1,0,-1) + B (0,1,-1) 均匀采样,其中 A 和 B 的范围均匀地从 0 到 1。

-A

【讨论】:

  • 关于 Simen 的解决方案,你说 "(1, 0, 0),一个同样有效的三元组,必须来自 (1, 0, 0)" .. I' d 说这是错误的:任何(a,0,0) for a in (0,1] 都会生成 (1,0,0)。
  • 很好,那是不正确的!这里有一个更好的解释:想象立方体 [0,1]^3;我们对这个立方体进行均匀采样,然后归一化为总和为 1。有效归一化点的三角形区域 (x+y+z=1; x,y,z>=0) 是某些集合的归一化后图像[0,1]^3 立方体中的点 - 即立方体中的所有点,它们也在连接原点和平面上该点的线上(生成的交点是一个线段)。但是通过(1/3, 1/3, 1/3)的线段是立方对角线(len rt(3)),而通过(1,0,0)的线段有len 1,所以前者是rt(3 ) 倍。
【解决方案3】:

生成两个介于 0 和 1 之间的随机数。 将它们分别除以 3。 第三个是1和两个随机三分之差:

void Main()
{
    Random r = new Random();
    double d1 = r.NextDouble() / 3.0;
    double d2 = r.NextDouble() / 3.0;
    double d3 = 1.0 - d1 - d2;
    System.Console.WriteLine(d1);
    System.Console.WriteLine(d2);
    System.Console.WriteLine(d3);
    System.Console.WriteLine(d1 + d2 + d3);
}

这会在 LINQPad 中输出以下内容:

0.0514050276878934
0.156857372489847
0.79173759982226
1

【讨论】:

  • 不会均匀分布。第三个数字大于其他数字。
【解决方案4】:

更新

  1. 创建一个包含 3 个随机数的向量 3
  2. 标准化向量

【讨论】:

  • @Marc:是的,我会将其更新为另一个答案。但总数必须为 1,所以这是我的第一个想法。
  • 和西门的方案一样,不是吗?
  • 我不确定这是否正确。如果我没记错我的数学,那么大小为 1 的向量的分量之和不一定为 1。
【解决方案5】:

Marnix 的回答略有不同:

  1. 从[0,1]生成一个随机数a
  2. 生成两个随机数。 x 来自 [0,a] 和 y 来自 [a,1]
  3. 将结果设置为xy-x1-y

【讨论】:

    【解决方案6】:

    有一种简单的方法可以做到这一点,但您需要能够生成一个统一的随机数。

    让 X 在 (0,2/3) 上一致。如果 X

    在此设置下,X、Y 和 Z 的总和为 1,它们都将具有相同的均匀 (0, 2/3) 边际分布,并且所有三个成对相关性均为 -(1/2)。

    【讨论】:

      【解决方案7】:

      1/2 方法:

      • 创建一个随机数列表,每个 0 到 1 的长度为 PARTS。
      • 总结列表
      • 将每个元素除以总和
      • 圆每个元素
      • 通过编辑第一个元素来计算浮点数学

      抱歉不懂C#,这里是python:

      import random
      import time
      
      PARTS       = 5
      TOTAL       = 10
      PLACES      = 3
      
      def random_sum_split(parts, total, places):
      
          a = []
          for n in range(parts):
              a.append(random.random())
          b = sum(a)
          c = [x/b for x in a]    
          d = sum(c)
          e = c
          if places != None:
              e = [round(x*total, places) for x in c]
          f = e[-(parts-1):]
          g = total - sum(f)
          if places != None:
              g = round(g, places)
          f.insert(0, g)
      
          log(a)
          log(b)
          log(c)
          log(d)
          log(e)
          log(f)
          log(g)
      
          return f   
      
      def tick():
      
          if info.tick == 1:
      
              start = time.time()
      
              alpha = random_sum_split(PARTS, TOTAL, PLACES)
      
              log('********************')
              log('***** RESULTS ******')
              log('alpha: %s' % alpha)
              log('total: %.7f' % sum(alpha))
              log('parts: %s' % PARTS)
              log('places: %s' % PLACES)
      
              end = time.time()  
      
              log('elapsed: %.7f' % (end-start))
      

      产量:

      Waiting...
      Saved successfully.
      [2014-06-13 00:01:00] [0.33561018369775897, 0.4904215932650632, 0.20264927800402832, 0.118862130636748, 0.03107818050878819]
      [2014-06-13 00:01:00] 1.17862136611
      [2014-06-13 00:01:00] [0.28474809073311597, 0.41609766067850096, 0.17193755673414868, 0.10084844382959707, 0.02636824802463724]
      [2014-06-13 00:01:00] 1.0
      [2014-06-13 00:01:00] [2.847, 4.161, 1.719, 1.008, 0.264]
      [2014-06-13 00:01:00] [2.848, 4.161, 1.719, 1.008, 0.264]
      [2014-06-13 00:01:00] 2.848
      [2014-06-13 00:01:00] ********************
      [2014-06-13 00:01:00] ***** RESULTS ******
      [2014-06-13 00:01:00] alpha: [2.848, 4.161, 1.719, 1.008, 0.264]
      [2014-06-13 00:01:00] total: 10.0000000
      [2014-06-13 00:01:00] parts: 5
      [2014-06-13 00:01:00] places: 3
      [2014-06-13 00:01:00] elapsed: 0.0054131
      

      【讨论】:

      • 这不是 C#。请注意问题上使用的标签。
      【解决方案8】:

      2/2 方法:

      • 创建0到1的随机数列表;缩放到总数
      • 从小到大对列表进行排序
      • 通过测量每个元素之间的空间来创建一个新列表 第一个列表
      • 对新列表中的每个元素进行四舍五入
      • 替换第一个元素以考虑浮点数

      对不起,我不知道 C# 在 python 中是这样的:

      import random
      import time
      
      PARTS       = 5
      TOTAL       = 10
      PLACES      = 3
      
      def random_sum_split(parts, total, places):
      
      
          a = [0.0, total]
          for i in range(parts-1):
              a.append(random.random()*total)
          a.sort()
          b = []
          for i in range(1,(parts+1)):
              b.append(a[i] - a[i-1])
          if places != None:    
              b = [round(x, places) for x in b]  
          c = b[-(parts-1):]
          d = total - sum(c)
          if places != None:
              d = round(d, places)
          c.insert(0, d)
      
          log(a)
          log(b)
          log(c)
          log(d)
      
          return c
      
      def tick():
      
          if info.tick == 1:
      
              start = time.time()
      
              alpha = random_sum_split(PARTS, TOTAL, PLACES)
      
              log('********************')
              log('***** RESULTS ******')
              log('alpha: %s' % alpha)
              log('total: %.7f' % sum(alpha))
              log('parts: %s' % PARTS)
              log('places: %s' % PLACES)
      
              end = time.time()  
      
              log('elapsed: %.7f' % (end-start))
      

      产量:

      Waiting...
      Saved successfully.
      [2014-06-13 00:01:00] [0.0, 1.3005056784596913, 3.0412441135728474, 5.218388755020509, 7.156425483589107, 10]
      [2014-06-13 00:01:00] [1.301, 1.741, 2.177, 1.938, 2.844]
      [2014-06-13 00:01:00] [1.3, 1.741, 2.177, 1.938, 2.844]
      [2014-06-13 00:01:00] 1.3
      [2014-06-13 00:01:00] ********************
      [2014-06-13 00:01:00] ***** RESULTS ******
      [2014-06-13 00:01:00] alpha: [1.3, 1.741, 2.177, 1.938, 2.844]
      [2014-06-13 00:01:00] total: 10.0000000
      [2014-06-13 00:01:00] parts: 5
      [2014-06-13 00:01:00] places: 3
      [2014-06-13 00:01:00] elapsed: 0.0036860
      

      【讨论】:

      • 关于这个确切问题的评论与其他评论相同。这个问题被标记为 C#,请用问题的语言回答。
      • 糟糕,从 python 线程重定向。理论仍然存在;显示了两种替代解决方案。我不懂 C#,但我会编辑/发布一个通用算法来帮助移植。
      • 它的格式可以更好,我不认为我可以从这里的信息亲自执行转换,但我很感激你的努力。我将删除我的反对票。
      • 最简单的形式:a = [0, total] + [random.random()*total for i in range(parts-1)]a.sort()b = [(a[i] - a[i-1]) for i in range(1, (parts+1))]
      【解决方案9】:

      以@Simen 和@Daren Thomas 的回答为基础,这里有一个服务函数,它返回具有统一随机值的双精度数列表,您可以在其中指定所需的数字数量、总和以及数字的数量数字:

              public static List<double> GetListOfRandomDoubles(int countOfNumbers, double totalSum, int digits)
              {
                  Random r = new Random();
      
                  List<double> randomDoubles = new List<double>();
                  double totalRandomSum = 0; 
      
                  for (int i = 0; i < countOfNumbers; i++)
                  {
                      double nextDouble = r.NextDouble();
                      randomDoubles.Add(nextDouble);
                      totalRandomSum += nextDouble;
                  }
      
                  double totalFactor = 1 / totalRandomSum;
                  totalFactor = totalFactor * totalSum;
      
                  for (int i = 0; i < randomDoubles.Count; i++)
                  {
                      randomDoubles[i] = randomDoubles[i] * totalFactor;
                      randomDoubles[i] = Math.Round(randomDoubles[i], digits);
                  }
      
                  double currentRandomSum = 0;
                  randomDoubles.ForEach(x => currentRandomSum += x);
                  randomDoubles[0] += totalSum - currentRandomSum;
      
                  return randomDoubles;
              }
      

      用法:

              // Get list of 7 random doubles that sum to 100, with up to 2 digits on each number
              List<double> randomDoubles = GetListOfRandomDoubles(7, 100, 2);
      

      返回:

      12.25, 19.52, 15.49, 16.45, 1.92, 13.12, 21.25

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-11-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-07-25
        • 1970-01-01
        • 2014-04-29
        相关资源
        最近更新 更多