c-生成范围外的随机数而无循环

时间:2018-06-21 23:27:58

标签: c random

在C语言中,我可以使用以下简单表达式生成范围内的随机数:

rand() % (max_number + 1 - minimum_number) + minimum_number

是否存在类似的基于非循环的表达式,可以生成范围之外的随机数?例如,如果我的范围是3到5,则我想要一个随机数,范围是0到2,或者是RAND_MAX的6。

3 个答案:

答案 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)可以生成从lohi的均匀分布的数字,则转换if(r>=rangemin) r+=rangesize会很好地工作,并且不会破坏分布。

相关链接:

Generating a uniform distribution of INTEGERS in C

http://eternallyconfuzzled.com/arts/jsw_art_rand.aspx

答案 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%的机会高于您的最小值。最大,无论两个间隔之间的大小差异如何。因此,除非您特别想要这种行为,否则其他解决方案最好将偏差降到最低。