c - 如何从一定范围内生成随机整数

标签 c random

这是之前发布的问题的后续问题:

How to generate a random number in C?

我希望能够生成特定范围内的随机数,例如 1 到 6 来模拟骰子的侧面。

我该如何去做呢?

最佳答案

到目前为止,所有答案在数学上都是错误的。返回 rand() % N 并不统一给出 [0, N) 范围内的数字,除非 N 将区间长度划分为rand() 返回(即 2 的幂)。此外,我们不知道 rand() 的模是否独立:它们有可能是 0, 1, 2, ...,这是统一的,但不是独立的非常随机。唯一合理的假设是 rand() 得出泊松分布:任何两个相同大小的非重叠子区间均可能且独立。对于一组有限的值,这意味着均匀分布,并且还确保 rand() 的值很好地分散。

这意味着更改rand()范围的唯一正确方法是将其划分为多个框;例如,如果 RAND_MAX == 11 并且您想要 1..6 的范围,则应将 {0,1} 分配给 1 ,{2,3} 到 2,依此类推。这些是不相交的、大小相等的区间,因此是均匀且独立分布的。

使用浮点除法的建议在数学上是合理的,但原则上存在舍入问题。也许 double 的精度足以使其工作;也许不是。我不知道,也不想弄清楚;无论如何,答案取决于系统。

正确的方法是使用整数运算。也就是说,您想要如下所示的内容:

#include <stdlib.h> // For random(), RAND_MAX

// Assumes 0 <= max <= RAND_MAX
// Returns in the closed interval [0, max]
long random_at_most(long max) {
  unsigned long
    // max <= RAND_MAX < ULONG_MAX, so this is okay.
    num_bins = (unsigned long) max + 1,
    num_rand = (unsigned long) RAND_MAX + 1,
    bin_size = num_rand / num_bins,
    defect   = num_rand % num_bins;

  long x;
  do {
   x = random();
  }
  // This is carefully written not to overflow
  while (num_rand - defect <= (unsigned long)x);

  // Truncated division is intentional
  return x/bin_size;
}

为了获得完全均匀的分布,循环是必要的。例如,如果给你 0 到 2 之间的随机数,而你只想要 0 到 1 之间的数字,你就继续拉,直到没有 2 为止;不难检查这是否以相同的概率给出 0 或 1。 nos 在其答案中给出的链接中也描述了此方法,尽管编码不同。我使用 random() 而不是 rand() 因为它具有更好的分布(如 rand() 的手册页所述) )。

如果你想获得默认范围[0, RAND_MAX]之外的随机值,那么你必须做一些棘手的事情。也许最方便的方法是定义一个函数 random_extend() ,它提取 n 位(使用 random_at_most())并返回 [ 0, 2**n),然后应用 random_at_most() 并用 random_extended() 代替 random() (和 2**n - 1 代替 RAND_MAX)来提取小于 2**n 的随机值,假设您有一个数字可以保存这样的值的类型。最后,当然,您可以使用 min + random_at_most(max - min) 获取 [min, max] 中的值,包括负值。

关于c - 如何从一定范围内生成随机整数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39352908/

相关文章:

c - 外部内联风险

java - 在不传递种子的情况下获取随机对象的种子?

java - 更好的算法?更好的阵列?

ember.js - Ember : Return randomized property value on each reload

java - 获取2个随机数的平均值 "30"

c++ - C++ 中的随机整数

c - 理解传递的这个参数

c - recvfrom - 如何将缓冲区转换为字符串

c - 在取消引用 void 指针后转换为 int 是否正确?

c - C混淆中的递归