如何从一个范围内生成一个随机整数 [英] How to generate a random integer number from within a range

查看:40
本文介绍了如何从一个范围内生成一个随机整数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

This is a follow on from a previously posted question:

如何在 C 中生成随机数?

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

I wish to be able to generate a random number from within a particular range, such as 1 to 6 to mimic the sides of a die.

我该怎么做?

推荐答案

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

All the answers so far are mathematically wrong. Returning rand() % N does not uniformly give a number in the range [0, N) unless N divides the length of the interval into which rand() returns (i.e. is a power of 2). Furthermore, one has no idea whether the moduli of rand() are independent: it's possible that they go 0, 1, 2, ..., which is uniform but not very random. The only assumption it seems reasonable to make is that rand() puts out a Poisson distribution: any two nonoverlapping subintervals of the same size are equally likely and independent. For a finite set of values, this implies a uniform distribution and also ensures that the values of rand() are nicely scattered.

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

This means that the only correct way of changing the range of rand() is to divide it into boxes; for example, if RAND_MAX == 11 and you want a range of 1..6, you should assign {0,1} to 1, {2,3} to 2, and so on. These are disjoint, equally-sized intervals and thus are uniformly and independently distributed.

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

The suggestion to use floating-point division is mathematically plausible but suffers from rounding issues in principle. Perhaps double is high-enough precision to make it work; perhaps not. I don't know and I don't want to have to figure it out; in any case, the answer is system-dependent.

正确的方法是使用整数算法.也就是说,您需要类似以下内容:

The correct way is to use integer arithmetic. That is, you want something like the following:

#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() 的手册页所述)).

The loop is necessary to get a perfectly uniform distribution. For example, if you are given random numbers from 0 to 2 and you want only ones from 0 to 1, you just keep pulling until you don't get a 2; it's not hard to check that this gives 0 or 1 with equal probability. This method is also described in the link that nos gave in their answer, though coded differently. I'm using random() rather than rand() as it has a better distribution (as noted by the man page for rand()).

如果你想获得默认范围[0, RAND_MAX]之外的随机值,那么你必须做一些棘手的事情.也许最权宜之计是定义一个函数 random_extended()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] 中的值,包括负值.

If you want to get random values outside the default range [0, RAND_MAX], then you have to do something tricky. Perhaps the most expedient is to define a function random_extended() that pulls n bits (using random_at_most()) and returns in [0, 2**n), and then apply random_at_most() with random_extended() in place of random() (and 2**n - 1 in place of RAND_MAX) to pull a random value less than 2**n, assuming you have a numerical type that can hold such a value. Finally, of course, you can get values in [min, max] using min + random_at_most(max - min), including negative values.

这篇关于如何从一个范围内生成一个随机整数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆