如何在范围内生成随机整数
继@Ryan Reich的回答之后,我想提供我的整理版本。第一个界限检查不是必需的,由于第二个界限检查,我将其改为迭代而不是递归。它返回范围[min,max]内的值,其中max> = min
且1+max-min
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); }
到目前为止,所有的答案都是错误的数学方法。返回rand() % N
不会均匀地产生一个范围在[0,N)
内的数字,除非N
将rand()
返回的区间长度分成几个部分(即是2的次幂)。此外,一个没有什么意义的假设是rand()
的模是独立的:可能是0,1,2...
,这是均匀但不太随机的。唯一合理的假设似乎是rand()
产生的一个泊松分配:同样大小的任何两个不重叠的子区间的可能性相等且相互独立。对于一组有限的值,这意味着均匀分布,也确保了rand()
的值被很好地分散。\n\n这意味着改变rand()
的范围的唯一正确方法是将其分成盒子;例如,如果RAND_MAX==11
,你想要一个1..6
的范围,则应将{0,1}
分配给1,将{2,3}
分配给2,以此类推。这些是不相交、大小相等的区间,因此是均匀和独立的分布。\n\n使用浮点除法的建议在数学上是可行的,但在原则上会有舍入问题。也许double
的精度足够高,使其工作;也可能不是。我不知道,也不想弄清楚;在任何情况下,答案取决于系统。\n\n正确的方法是使用整数算术。也就是说,你想要像下面这样的东西:\n\n
#include// 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; }
\n\n循环是必需的,以获得完全均匀的分布。例如,如果你得到的随机数是从0到2,并且你只想要从0到1的随机数,你只需要不断地拉取,直到你不再得到一个2;不难检查这样做可以等概率地得到0或1。这种方法也在Nos答案中描述,虽然编码方式有所不同。我使用random()
而不是rand()
,因为它的分布更好(如rand()
的手册所述)。\n\n如果你想要在默认范围[0,RAND_MAX]
之外得到随机值,那么你必须做一些巧妙的事情。可能最简便的方法是定义一个函数random_extended()
,使用random_at_most()
提取n
个比特,并返回[0,2 ** n)
,然后使用random_at_most()
将random()
替换为random_extended()
(将2 ** n-1
替换为RAND_MAX
),以获得小于2 ** n
的随机值,假设你有一个能够容纳这样的值的数字类型。最后,当然,你可以使用min + random_at_most(max - min)
获得[min,max]
中的值,包括负值。