【问题标题】:How to get a random number from 0 to N-1 with a "same probability" in C? [duplicate]如何在C中以“相同概率”获得从0到N-1的随机数? [复制]
【发布时间】:2014-02-25 08:03:07
【问题描述】:

我知道这可能是一个“老”问题,但我想关注概率。

我的第一个问题是: 在 C 中,rand() 将给出一个从0RAND_MAX 的数字,这个区间中的每个数字是否有相同的概率被rand() 选择?

第二个问题: 如果rand() 让从0RAND_MAX 的每个数字具有相同(或大致相同)的概率被选择,当我想获得一个从0 到N-1 的随机数(N-1

rand()%N

但是如果RAND_MAX不是N的倍数,那么从0到N-1随机数的概率可能不一样

例如,假设 RAND_MAX=150 和 N=100,当我做rand()%100 时,从 0 到 49 的数字将比从 50 到 99 的数字更有可能被选择,因为 150 不是100.

C中有没有算法或函数可以让每个随机数有相同的概率被选中?

【问题讨论】:

  • 您可以除以双倍:rand()/(double)RAND_MAX 并乘以您的范围,但这还有一些其他问题,也许有人可以指出。
  • 只需将“可能性”替换为“概率”,考虑到任何伪 RNG(随机数生成器)的理论限制,这两个问题的答案都是肯定的。
  • 我不经常向微软推荐任何东西,但你应该注意:channel9.msdn.com/Events/GoingNative/2013/…
  • rand() / (double)RAND_MAX 将随机数缩放到 [0,1] 范围有什么问题。这种方式应该避免偏见
  • @Brandin 看 6:30 左右的视频。

标签: c algorithm random


【解决方案1】:

撇开rand()%N 是获取0..N-1 范围内的随机数的一种非常糟糕的方式不谈,您提出的问题非常简单。找到满足M <= RAND_MAXM % N == 0 的最大数,M 说。然后,当您调用rand() 时,如果它是>= M,则拒绝一个值并再次调用rand(),直到您得到一个值< M

但是这种特殊的细微差别是没有意义的,因为rand()%N 将有无可救药的偏见。您需要尽可能多地使用rand() 返回的位。

【讨论】:

    【解决方案2】:

    假设rand() 本身是均匀分布的(并不总是一个好的假设。),根据需要再次调用rand()

    基于Trying to take 1 random digit at a time

    #include <stdlib.h>
    int rand_Flat_Distribution_0_to_Nm1(int N) {
      assert(N <= RAND_MAX);
      assert(N > 0);
      int rmax = RAND_MAX - (RAND_MAX % N) - 1;
      int r;
      while ((r = rand()) > rmax);
      return r%N;
    }
    

    示例: 如果 RAND_MAX 是 32767 并且 N 是 100,则 rmax 的值将是 32699。将抛出 32700 到 32767 范围内的任何随机值,并获取一个新的随机值,消除偏差 %N通常会导致。

    这并不能弥补rand() 的不足。 C 没有指定rand() 的质量,只是它生成的值0RAND_MAXRAND_MAX最少 32767。

    对于大于RAND_MAXN 值,需要不同的解决方案。

    【讨论】:

      【解决方案3】:

      在使用rand() 时,无法确定结果的真正随机性,这在很大程度上取决于系统提供随机数的方式。如果您搜索,可以使用各种 3rd 方伪随机数生成器 (PRNG) 包,但如果随机性非常重要,那么涉及硬件的解决方案可能会更好。

      你是对的,简单地修改多余的部分会给价值观带来偏见。您可以通过将随机结果转换为浮点数来避免这种情况,方法是除以随机生成器可以提供的最大值,然后将其乘以您愿意处理的值范围内的元素数。如果您的范围不是以 0 开头,那么您将添加您期望的基值。

      【讨论】:

      • C 规范没有强制要求特定的实现,但对 rand/srand 夫妇的约束几乎可以保证它将是一个 LCG。但最重要的是,通过 FP 返回整数仍然会给你一个有偏差的结果,尽管方式不太明显。
      【解决方案4】:

      这是使用二进制表示均匀地做到这一点的方法:-

      1. generate logN bits using rand()%2
      2. construct decimal number out of them.
      3. check with N
      4. if less than N return else repeat 1 - 3.
      

      注意:-我认为除了拒绝之外没有其他方法可以生成均匀分布,因为重复使用大于或等于 N 的一次总是会导致概率不平衡

      时间复杂度:- 如果使用真正的随机生成器,那么循环 1-3 平均只有两次运行,这意味着 2*logN 平均情况下是 O(logN*Trand)

      这是一个支持我的算法的 Java 实现:-

      public static void getrand(int n,int k) {
      
              int range = n-1;
              int bits = 0;
              Random r = new Random();
              while(range>0) {
                  range = range>>1;
                  bits++;
              }
              System.out.println("bits: "+bits);
              //int[] freq = new int[n];
              int count = 0;
              for(int i=0;i<k;i++) {
                  int steps  = 0;
                  while(true) {
                      int randomNum = 0;
                      steps++;
                      for(int j=0;j<bits;j++) {
      
                          randomNum = randomNum<<1|(r.nextInt(2));
                      }
                      if(randomNum<n) {
                          System.out.println("Random Number: "+randomNum+" steps: "+steps);
                          //freq[randomNum]++;
                          count = count + steps;
                          break;
                      }
      
                  }
              }
              System.out.println("average steps: "+(float)(count)/k);
      
          }
      

      n = 小于您需要生成值的数字。 k = 总计 number 你需要生成的随机数。

      【讨论】:

      • “构造十进制数”?
      • @OliCharlesworth 如果我使用 %10 或 %2 构造它并不重要,因为它需要相同的时间,因为它平均需要 5*log10(N) 来构造十进制数,相比之下它需要 1.5 *log2(N) 表示二进制数,当带到 base10 时会给出 5*log10(N) 。此外,%2 比 %10 更有可能提供更好的分布。
      【解决方案5】:

      理论上,对于任意 N 值,这样的算法是不可能的。

      如果你调用 rand() X 次,有 RAND_MAX^X 个可能的结果。除非 N 的主要因数都是 RAND_MAX 的因数,否则 RAND_MAX^X 不可能被 N 整除。它们不必被彼此整除才能实现均匀分布,但应该存在这样的 Y 所以RAND_MAX^Y 可以被 N 整除。所以 RAND_MAX=12 和 N=9 有效(12^2 可以被 9 整除),但 RAND_MAX=12 和 N=10 不能(无论 X 是什么,12^X 都赢了'不能被 10 整除)。

      也就是说,X 越大,使用模公式越接近均匀分布。如果您调用 rand() X 次,计算 rand[0]*RAND_MAX^(X-1) + rand[1]*RAND_MAX^(X-2) + ... rand[X-1],并取模 N,因为 X 趋于无穷大,概率分布趋于均匀。这是你能得到的最好的。

      【讨论】:

      • 看来 OP 已经意识到模数方法存在缺陷;他们要求一个没有缺陷的算法。
      • 我的意思是,任意 N,不可能有一个。
      • 当然有;只需使用基于拒绝的方法。
      • 你的意思是——拒绝一些结果并重复这个过程?在这种情况下,理论上它的最大运行时间是无限的。
      • 确实如此,但永远运行的概率渐近为零。在实践中,这很好,并且是大多数库使用的方法。
      猜你喜欢
      • 2016-10-15
      • 2011-01-16
      • 2015-06-05
      • 2013-04-21
      • 2020-10-29
      • 1970-01-01
      • 2011-12-25
      • 2021-10-09
      • 2018-11-26
      相关资源
      最近更新 更多