如何从一个范围内生成一个随机整数 [英] How to generate a random integer number from within a range
问题描述
这是之前发布的问题的后续:
This is a follow on from a previously posted question:
我希望能够在特定范围内生成一个随机数,例如 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屋!