发布于 2011-07-28 07:20:38
到目前为止,所有答案在数学上都是错误的。除非N
将rand()
返回的区间长度除以(即是2的幂),否则返回rand() % N
不会一致地给出[0, N)
范围内的数字。此外,人们不知道rand()
的模块是否独立:它们可能是0, 1, 2, ...
的,这是一致的,但不是非常随机的。唯一合理的假设是,rand()
分布为泊松分布:任何两个大小相同的不重叠子区间的概率相等且相互独立。对于有限的值集,这意味着均匀分布,并确保rand()
的值很好地分散。
这意味着更改rand()
范围的唯一正确方法是将其分成方框;例如,如果RAND_MAX == 11
和您想要一个1..6
范围,则应将{0,1}
指定为1,将{2,3}
指定为2,依此类推。这些是不相交的、大小相等的间隔,因此是均匀且独立分布的。
使用浮点除法的建议在数学上是合理的,但原则上存在舍入问题。也许double
是高精度的,足以让它工作;也许不是。我不知道,我也不想弄清楚;在任何情况下,答案都取决于系统。
正确的方法是使用整数运算。也就是说,您需要的内容如下所示:
#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()
的手册页所示)。
如果您想获得默认范围[0, RAND_MAX]
之外的随机值,那么您必须做一些棘手的事情。也许最方便的方法是定义一个函数random_extended()
,它拉取代码位(使用random_at_most()
)并在[0, 2**n)
中返回,然后使用random_at_most()
和random_extended()
代替random()
(和2**n - 1
代替RAND_MAX
),以拉取一个小于< n
>d28的随机值,假设您有一个可以容纳这样的值的数值类型。最后,当然,您可以使用min + random_at_most(max - min)
获取[min, max]
中的值,包括负值。
发布于 2013-07-10 01:27:56
根据@Ryan Reich的回答,我想我应该提供我的清理过的版本。第一个边界检查是不需要的,因为第二个边界检查,我已经使它迭代而不是递归。它返回最小、最大范围内的值,其中max >= min
和1+max-min < RAND_MAX
。
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);
}
发布于 2011-05-05 14:44:43
如果您知道某个范围的最大值和最小值,并且希望生成包含在该范围之间的数字,则可以使用以下公式:
r = (rand() % (max + 1 - min)) + min
https://stackoverflow.com/questions/2509679
复制相似问题