最佳答案
到目前为止,所有答案在数学上都是错误的。返回 rand() % N
并不统一给出 [0, N)
范围内的数字,除非 N
将区间长度划分为rand()
返回(即 2 的幂)。此外,我们不知道 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_extend()
,它提取 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]
中的值,包括负值。
关于c - 如何从一定范围内生成随机整数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39352908/