首页 > 解决方案 > rand 函数在每次运行时都给我相同的结果,即使我调用了 srand(time(NULL))

问题描述

我有一个问题,我想用来rand()获取 0 到 6 之间的随机数,但每次运行它总是给我 4,即使我打电话srand(time(NULL))

#include <stdlib.h>
#include <stdio.h>
#include <time.h>

int main(void)
{
    srand(time(NULL));

    int rd = rand() % 7;

    printf("%d\n", rd);
    return (0);
}

4每次运行时输出

标签: crandom

解决方案


您的代码有两个基本问题,它们结合起来会产生您正在经历的奇怪结果。

几乎任何人都会警告您有关rand()界面的使用。事实上,Mac OS 手册页本身以警告开头:

$ man rand
NAME
     rand, srand, sranddev, rand_r -- bad random number generator

是的,这是一个糟糕的随机数生成器。除其他问题外,糟糕的随机数生成器可能难以播种。

但是说到播种,这里还有另一个问题,可能讨论得较少,但仍然很重要: 不要time(NULL)用来播种你的随机数生成器

链接的答案对此进行了更详细的说明,但基本问题很简单:time(NULL)不经常更改的值(如果经常以纳秒为单位测量),并且在更改时变化不大。因此,您不仅依赖于程序不经常运行(或至少每秒少于一次),而且还依赖于随机数生成器从稍微不同的种子产生完全不同的值。也许一个好的随机数生成器可以做到这一点,但我们已经确定这rand()是一个糟糕的随机数生成器。

好的,这一切都很笼统。具体问题有点有趣,至少出于学术目的(学术,因为实际的解决方案总是“使用更好的随机数生成器用良好的随机种子播种”)。这里的确切问题是您正在使用rand() % 7.

这是一个问题,因为 Mac OS / FreeBSD 的实现rand()是将种子乘以 7 的倍数。因为该乘积以 2 32为模(不是 7 的倍数)减少,所以第一个随机数的值以 7 为模缓慢增加种子产生的数字最终会改变,但它必须等到溢出量发生变化。

这是代码的链接。本质在这三行:

    hi = *ctx / 127773;
    lo = *ctx % 127773;
    x = 16807 * lo - 2836 * hi;

其中,根据评论,“计算 [s] x = (7^5 * x) mod (2^31 - 1) 而不会溢出 31 位。” x是最终将返回的值(模 2 32),它也是下一个种子。*ctx是当前的种子。

正如评论所说,16807 是 7 5,显然可以被 7 整除。而 2836 mod 7 是 1。所以根据模运算规则:

x mod 7 = (16807 * lo) mod 7 - (2836 * hi) mod 7
        =          0         -       hi mod 7   

该值仅取决于hi,即seed / 127773。因此hi,每 127773 个滴答声恰好改变一次。由于结果time(NULL)是以秒为单位的,因此这是 127773 秒的一个变化,大约是一天半。因此,如果您每天运行一次程序,您会注意到第一个随机数有时与前一天相同,有时少一个。但是你运行它的频率要高得多,即使你在运行之间等待几秒钟,所以你每次都看到相同的第一个随机数。最终它会下降,然后你会看到一系列 3s 而不是 4s。


推荐阅读