如何在范围内生成随机整数

15 浏览
0 Comments

如何在范围内生成随机整数

这是对之前发布的一个问题的跟进:

如何在C中生成随机数?

我希望能够在特定范围内生成随机数,比如1到6,以模拟骰子的各个面。

我该怎么做呢?

admin 更改状态以发布 2023年5月20日
0
0 Comments

继@Ryan Reich的回答之后,我想提供我的整理版本。第一个界限检查不是必需的,由于第二个界限检查,我将其改为迭代而不是递归。它返回范围[min,max]内的值,其中max> = min1+max-min

unsigned int rand_interval(unsigned int min, unsigned int max)
{
    int r;
    const unsigned int range = 1 + max - min;
    const unsigned int buckets = RAND_MAX / range;
    const unsigned int limit = buckets * range;
    /* Create equal size buckets all in a row, then fire randomly towards
     * the buckets until you land in one of them. All buckets are equally
     * likely. If you land off the end of the line of buckets, try again. */
    do
    {
        r = rand();
    } while (r >= limit);
    return min + (r / buckets);
}

0
0 Comments

到目前为止,所有的答案都是错误的数学方法。返回rand() % N不会均匀地产生一个范围在[0,N)内的数字,除非Nrand()返回的区间长度分成几个部分(即是2的次幂)。此外,一个没有什么意义的假设是rand()的模是独立的:可能是0,1,2...,这是均匀但不太随机的。唯一合理的假设似乎是rand()产生的一个泊松分配:同样大小的任何两个不重叠的子区间的可能性相等且相互独立。对于一组有限的值,这意味着均匀分布,也确保了rand()的值被很好地分散。\n\n这意味着改变rand()的范围的唯一正确方法是将其分成盒子;例如,如果RAND_MAX==11,你想要一个1..6的范围,则应将{0,1}分配给1,将{2,3}分配给2,以此类推。这些是不相交、大小相等的区间,因此是均匀和独立的分布。\n\n使用浮点除法的建议在数学上是可行的,但在原则上会有舍入问题。也许double的精度足够高,使其工作;也可能不是。我不知道,也不想弄清楚;在任何情况下,答案取决于系统。\n\n正确的方法是使用整数算术。也就是说,你想要像下面这样的东西:\n\n

#include  // 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;
}

\n\n循环是必需的,以获得完全均匀的分布。例如,如果你得到的随机数是从0到2,并且你只想要从0到1的随机数,你只需要不断地拉取,直到你不再得到一个2;不难检查这样做可以等概率地得到0或1。这种方法也在Nos答案中描述,虽然编码方式有所不同。我使用random()而不是rand(),因为它的分布更好(如rand()的手册所述)。\n\n如果你想要在默认范围[0,RAND_MAX]之外得到随机值,那么你必须做一些巧妙的事情。可能最简便的方法是定义一个函数random_extended(),使用random_at_most()提取n个比特,并返回[0,2 ** n),然后使用random_at_most()random()替换为random_extended()(将2 ** n-1替换为RAND_MAX),以获得小于2 ** n的随机值,假设你有一个能够容纳这样的值的数字类型。最后,当然,你可以使用min + random_at_most(max - min)获得[min,max]中的值,包括负值。

0