如果范围足够小,那么使用通常的取模方法应该不会有问题
int GetRandomInt(int Min, int Max)
{
return (rand()%(Max-Min+1))+Min;
}
(where Min
a Max
指定一个闭区间,[Min
, Max
])
每掷一次骰子就调用一次。别忘了打电话srand(time(NULL))
在应用程序开始时(在开始时only,而不是每次您想要获得随机数时)来为随机数生成器提供种子。
如果范围开始变大,您可能不得不面临两个问题:
一、范围rand()
显然不是 [0, +∞),而是 [0,RAND_MAX
], 在哪里RAND_MAX
is a #define
保证至少为 32767。如果你的范围 (Max-Min
) 跨越RAND_MAX
,那么,使用此方法,您将得到一些返回概率为零的数字。
这更微妙:假设RAND_MAX
比你的范围大,但不是that更大,让我们这么说RAND_MAX==1.5*/(Max-Min)
。
在这种情况下,结果的分布不会均匀:rand()
返回 [0,RAND_MAX
] (并且此范围内的每个整数应该是等概率的),但是您将使用剩余的除法(Max-Min)
。这意味着所需范围前半部分的数字被返回的概率是其他数字的两倍:它们实际上可以来自第一个and第三个三分之一的rand()
范围,而所需范围的后半部分只能来自第二个三分之一rand()
range.
这对你来说意味着什么?
可能什么也没有。如果您想做的只是一个掷骰子模拟器,那么您可以使用取模方法而不会出现任何问题,因为涉及的范围很小,而第二个问题尽管是still目前,这几乎是无关紧要的:假设你的范围是 3 并且MAX_RAND
32767:从 0 到 32765,0、1 和 2 具有相同的概率,但上升到 32767 0 和 1 获得一个潜在的退出,这几乎是无关紧要的,因为它们是从完美的 1/3 (10922/32766=0,333 ...) 每个 1 的概率为 2 (~0,33332) 的 10922/32767 和 0 和 1 的 10923/32767 (~0,33335)(假设rand()
提供了一个perfect分配)。
总之,为了克服这些问题,一个常用的方法是“拉伸”rand()
使用如下方法将范围扩展到更广泛的范围(或将其压缩到更小的范围):
int GetRandomInt(int Min, int Max)
{
return (int)(((double)rand())/MAX_RAND*(Max-Min))+Min;
}
基于等价性rand():MAX_RAND=X:(Max-Min)
。转换为 double 是必要的,否则之间的整数除法rand()
并且它的最大值将始终产生 0(或者在极少数情况下产生 1)rand()==MAX_RAND
);如果 MAX_RAND 很小并且范围也不是太宽,可以用整数运算来先执行乘积,否则溢出的风险很高。
我怀疑,如果输出范围大于rand()
,“拉伸”和 fp 值截断(由于转换为 int)以某种方式影响分布,但只是局部影响(例如,在小范围内,您可能永远不会得到某个数字,但全局分布看起来不错)。
请注意,此方法有助于克服 C 标准库随机数生成器的分散限制,即返回值的低位随机性较低 - 顺便说一句,这些位是您在执行模运算时使用的那些输出范围小。
然而,请记住,C 标准库 RNG 是一个简单的库,它努力遵守“简单”的统计规则,因此很容易预测;当需要“严重”随机数时(例如密码学),不应该使用它。对于此类需求,有专门的 RNG 库(例如RNG partGNU 科学图书馆),或者,如果您需要really随机的东西,有几个real随机数服务(最著名的之一是this),它不使用数学伪 RNG,而是从真实的随机源(例如放射性衰变)获取数字。