如何从一定范围内生成随机整数 [英] 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.
我该怎么做?
推荐答案
到目前为止,所有答案在数学上都是错误的.除非N
将rand()
返回的区间的长度除以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屋!