Rya*_*ich 166
到目前为止,所有答案在数学上都是错误的.除非除去返回的区间的长度(即2的幂),否则返回rand() % N不会均匀地给出范围中的数字.此外,人们不知道模量是否是独立的:它们可能是去的,它是均匀的但不是非常随机的.唯一合理的假设是推出泊松分布:任何两个相同大小的非重叠子区间同样可能且独立.对于一组有限的值,这意味着均匀分布,并且还确保分散的值很好.[0, N)Nrand()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;
}
Run Code Online (Sandbox Code Playgroud)
循环是获得完美均匀分布所必需的.例如,如果给出0到2之间的随机数,并且只需要0到1之间的随机数,那么你只需继续拉动,直到你得不到2; 不难检查这是否给出0或1的概率相等.这个方法也在他们的回答中给出的链接中描述,尽管编码方式不同.我正在使用random()而不是rand()因为它具有更好的分布(如手册页所述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, max]使用中的值min + random_at_most(max - min),包括负值.
the*_*ter 33
继@Ryan Reich的回答后,我想我会提供清理版本.给定第二个边界检查不需要第一个边界检查,并且我已经迭代而不是递归.它返回[min,max]范围内的值,其中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);
}
Run Code Online (Sandbox Code Playgroud)
小智 19
如果您知道范围的最大值和最小值,并且希望生成范围之间的数字,则以下是公式:
r = (rand() % (max + 1 - min)) + min
Run Code Online (Sandbox Code Playgroud)
nos*_*nos 17
unsigned int
randr(unsigned int min, unsigned int max)
{
double scaled = (double)rand()/RAND_MAX;
return (max - min +1)*scaled + min;
}
Run Code Online (Sandbox Code Playgroud)
请参阅此处了解其他选项.
Arm*_*est 12
你不会这样做:
srand(time(NULL));
int r = ( rand() % 6 ) + 1;
Run Code Online (Sandbox Code Playgroud)
%是模数运算符.基本上它将除以6并返回余数...从0 - 5
对于那些理解偏差问题但不能忍受基于拒绝的方法的不可预测的运行时间的人来说,这个系列在[0, n-1]区间中产生逐渐减少偏差的随机整数:
r = n / 2;
r = (rand() * n + r) / (RAND_MAX + 1);
r = (rand() * n + r) / (RAND_MAX + 1);
r = (rand() * n + r) / (RAND_MAX + 1);
...
Run Code Online (Sandbox Code Playgroud)
它通过合成高精度定点随机数i * log_2(RAND_MAX + 1)位(其中i是迭代次数)并执行长乘法来实现n.
当比特数足够大时n,偏差变得无比小.
如果RAND_MAX + 1小于n(如在这个问题中),或者它不是2的幂,则无关紧要,但是如果RAND_MAX * n大则必须小心避免整数溢出.
这是一个比 Ryan Reich 的解决方案更简单的算法:
/// Begin and end are *inclusive*; => [begin, end]
uint32_t getRandInterval(uint32_t begin, uint32_t end) {
uint32_t range = (end - begin) + 1;
uint32_t limit = ((uint64_t)RAND_MAX + 1) - (((uint64_t)RAND_MAX + 1) % range);
/* Imagine range-sized 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. */
uint32_t randVal = rand();
while (randVal >= limit) randVal = rand();
/// Return the position you hit in the bucket + begin as random number
return (randVal % range) + begin;
}
Run Code Online (Sandbox Code Playgroud)
Example (RAND_MAX := 16, begin := 2, end := 7)
=> range := 6 (1 + end - begin)
=> limit := 12 (RAND_MAX + 1) - ((RAND_MAX + 1) % range)
The limit is always a multiple of the range,
so we can split it into range-sized buckets:
Possible-rand-output: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Buckets: [0, 1, 2, 3, 4, 5][0, 1, 2, 3, 4, 5][X, X, X, X, X]
Buckets + begin: [2, 3, 4, 5, 6, 7][2, 3, 4, 5, 6, 7][X, X, X, X, X]
1st call to rand() => 13
? 13 is not in the bucket-range anymore (>= limit), while-condition is true
? retry...
2nd call to rand() => 7
? 7 is in the bucket-range (< limit), while-condition is false
? Get the corresponding bucket-value 1 (randVal % range) and add begin
=> 3
Run Code Online (Sandbox Code Playgroud)