在C语言中,我可以使用以下简单表达式生成范围内的随机数:
rand() % (max_number + 1 - minimum_number) + minimum_number
是否存在类似的基于非循环的表达式,可以生成范围之外的随机数?例如,如果我的范围是3到5,则我想要一个随机数,范围是0到2,或者是RAND_MAX的6。
答案 0 :(得分:4)
这将起作用:
int r = rand() % (maxval+1 - rangesize);
if(r>=rangemin)
r+=rangesize;
在您的示例中,rangesize
将为3,因为要避免的范围内有三个数字,而rangemin
将为3,因为它是该范围内的最小数字。
这将产生[0, maxval]
范围内的随机数,除了[rangemin, rangemin+rangesize-1]
范围内的数字
但是,请注意,使用rand() % x
通常会导致分布不均,因此,如果需要考虑的话,请考虑一下。感谢rici指出这一点。有关更多信息,请参见他的答案。
但是假设您有一个函数r(lo, hi)
可以生成从lo
到hi
的均匀分布的数字,则转换if(r>=rangemin) r+=rangesize
会很好地工作,并且不会破坏分布。
相关链接:
答案 1 :(得分:2)
假设您想要一个无偏的随机数,则前提不正确。而且因为前提不正确,所以没有类似的解决方案。或者,更好地说,因为提供的代码产生了偏差样本,所以任何超出范围的类似采样代码也会产生偏差样本。
在C语言中,我可以使用以下简单表达式生成范围内的随机数:
rand() % (max_number + 1 - minimum_number) + minimum_number
如果范围的大小相对于rand()
返回的可能值的范围的大小,则或多或少会起作用。如果rand()
本身是无偏的,并且期望范围的大小是RAND_MAX + 1
的一个因数,它只会产生一个真正无偏的值。由于RAND_MAX + 1
通常是2的幂,因此唯一可以产生无偏选择的范围大小也是2的幂。
使用鸽子洞原理很容易看到这一点。想象有s
个鸽子洞,每个鸽子洞对应于所需范围内的值。 (当然,s
必须为max - min + 1
。)现在必须将RAND_MAX + 1
可能产生的每个rand()
值放入其中一个信孔。由于这些值中的每一个都具有相同的概率,并且选择一个鸽洞的概率是其内容的概率之和,因此,无偏结果要求所有鸽洞都具有相同数量的值,因此仅当s
是可能值数量的一个因素时。
在不常见的情况下,RAND_MAX + 1
是32768(例如Windows),如果s
是6(一圈骰子),那么四只鸽子中的每只鸽子都会有5461个值-holes,另两个中的5462值。这种偏见并不大,但不会通过博彩专员的检查。
当所需范围接近RAND_MAX + 1
时,情况会更加剧烈,如果排除较小范围,则会出现这种情况。在那种情况下,大多数鸽子洞只有一个值,少数幸运的鸽子洞每个都有两个值,因此被拣选的可能性是原来的两倍。
最简单的解决方法是涉及循环的“拒绝采样”。如果这些值之一出现,我们通过再次调用s % (RAND_MAX + 1)
来拒绝rand()
可能返回的rand()
。 (拒绝哪个值无关紧要,但拒绝小于s % (RAND_MAX + 1)
的值很简单。)在最坏的情况下,将拒绝将近一半的可能返回值,并且循环将仅运行一次平均。在更常见的情况下,它几乎不会运行,分支预测会将其成本降低到很少。
答案 2 :(得分:0)
一个简单的解决方案是:
int b = rand() % 2, nr;
if (b)
nr = rand() % min; // [0, min - 1]
else
nr = rand() % (RAND_MAX - max) + max + 1; // [max + 1, RAND_MAX]
为了避免在一定的时间间隔内出现偏差(对于不同的数字表示不同的概率),您仍然必须使用循环。您可能需要检查here所提供的答案。
编辑:正如klutt指出的那样,此解决方案本身会带来一些偏差,因为获得的数字有50%的机会低于您的最小值,而50%的机会高于您的最小值。最大,无论两个间隔之间的大小差异如何。因此,除非您特别想要这种行为,否则其他解决方案最好将偏差降到最低。