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

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

问题描述

这是先前发布的问题的后续内容:

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.

我该怎么做?

推荐答案

到目前为止,所有答案在数学上都是错误的.除非Nrand()返回的区间的长度除以2(即2的幂),否则返回rand() % N不会统一给出[0, N)范围内的数字.此外,人们不知道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天全站免登陆