【问题标题】:Why does the first random value just increase every time I run the code?为什么每次运行代码时第一个随机值都会增加?
【发布时间】:2020-06-07 10:57:45
【问题描述】:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(int argc, char *argv[]) {

    srand((unsigned)time(NULL));
    printf("%d\n", rand()%100+1);
    printf("%d", rand()%100+1);

    return 0;
}

我已经运行了很多次,每次第一个值都稳定增加,比如2 5 9 17 24 30 ..... 91 97 3 7 12(但值总是不同)。奇怪的是,第二个值执行正常,可能是什么原因?

我正在使用 Dev-C++ 和 TDM-GCC 4.9.2

【问题讨论】:

  • 您使用的是什么平台和编译器版本? rand() 的实施通常很糟糕。如果可以,请使用random(),并使用srandom() 播种。
  • @Thomas 我正在使用 Dev-C++ 和 TDM-GCC 4.9.2。
  • 使用像大多数rand() 实现使用的简单线性同余生成器,像时间一样稳定增加的种子值将产生稳定增加的第一个数字,是的。

标签: c random random-seed


【解决方案1】:

您说的非常对,我也不知道发生这种情况的确切或正确原因。

但我用以下代码解决了这个问题:

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

int main(int argc, char *argv[])
{
    srand(time(NULL));

    for (;;) // running infinitely here until program's interrupted
    {
        printf("%d\n", rand() % 100 + 1);
        printf("%d", rand() % 100 + 1);
        printf("\n---\n");
        sleep(1);
    }
    return 0;
}

如果您将srand(time(NULL)) 放在无限循环之外,并将其他所有内容放在其中,它就会正常工作。在此之后,您将在每次迭代中随机更改这两个值,而不仅仅是增加(第一个值)。

【讨论】:

  • 我刚才所做的一切都是将这两个printf() 插入到循环中。
【解决方案2】:

您的根本原因可能是 C 的旧随机数生成器并不是那么好。在我看来,它的主要用例是质量不重要,或者您需要能够重复“随机数”系列以确保测试结果等可以随着时间的推移进行很好的比较。这些“伪随机数生成器”一直是需要良好随机数的项目的痛处。

如果您需要真正的好东西,您可以使用以下方法获取随机数:

  1. 在最流行的操作系统上发现了基于熵的功能,例如类 unix 系统上的 /dev/urandom 系列随机设备。 Microsoft Windows 也有一些东西,但我忘记了。
  2. Internet 随机数服务,如 random.org(显然不适用于大容量号码)。他们有 API,因此程序可以自动化体验,这将是有教育意义的,但一定要遵循指南。
  3. 如果您真的想要质量或数量,硬件随机数生成器(搜索:usb 随机数)。

从历史上看,仅使用 rand() 值的低位往往会放大缺陷,这可能有助于获得您所看到的。我已经发布了第二个答案,该答案涉及改进 rand() 性能的具体细节。

【讨论】:

    【解决方案3】:

    试运行

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    int main(int argc, char *argv[]) {
        unsigned i;
        for (i=0; i<100; ++i) {
            srand(i);
            printf("%d\n", rand()%100);
        }
    
        return 0;
    }
    

    如果你得到一个纯粹的递增列表,那是因为你的随机性在第一次运行时返回种子。

    要修复它,请在 srand 之后调用 rand() 并忽略其值。它似乎会以某种方式改善随机性。

    【讨论】:

    • 成功了。但我就是不明白为什么我的代码中的第二个值没有受到种子相关问题的影响。
    • @rookie,你正在使用一个随机种子,它是调用time(3) 函数的结果,这使得你的种子(因此,第一个随机值)随着时间单调增加,而且根本不是随机的:)
    • @LuisColorado 我不知道。为什么我的代码中的第二个值不会发生这种情况?第二个值似乎很随机。
    • @rookie 在返回第一个随机数后运行了一些额外的函数,使它看起来很随机。
    • @rookie,因为第一个是你提供的,基于一天中的时间(所以它显示单调递增),但第二个是rand() 例程产生的第一个伪随机数,所以它必须显示一些随机性(一些不可预测性)
    【解决方案4】:

    除了我在关于更广泛的随机数问题的第一个答案中提到的大多数 rand() 实现的缺陷性质之外,您的 %100 中还有一个微妙的问题。由于 RAND_MAX 通常是 2 的幂,例如 32767,因此仅使用最后两位数字会稍微偏爱较低的值,因为可能会丢失 68-99 一些调用。要获得统一的 00-99 分布,您需要转到 32799,这不会发生。

    一个更好的解决方案是做一些事情来获得一个从 0.0 到 0.999999 的随机值......以一种使用所有位的方式然后扩展:

     //未测试代码警告:
    双 rand01 = (double)(rand()%RAND_MAX) / (double)RAND_MAX; // 取零直到但不包括一
    printf("%d\n", 1 + int(100.0 * rand01));

    这会使用返回的随机数中的所有可用位。

    软件生成随机数一直是计算机的痛点。

    【讨论】:

    • “仅使用最后两位数字会稍微偏向于较低的值,因为可能会丢失 68-99 一些调用”
    • 你的意思是它可能会在某些调用中丢失这些值吗?
    • 不像以前那样松散它们,至少对于 16 位值来说,我使用它是为了简单。不同的整数类型有不同的范围。非负符号值从全零到 0,然后是全 1。对于 32 位值,它将贯穿 0-2147483647,它没有 48-99 作为最后一个值。这个范围现在变得如此之大,以至于在统计上这些缺失的意义很小。然而,使用低位仍然是一个糟糕的主意。如果您想了解有关整数的更多详细信息,请查看 stdint.h(如 en.wikibooks.org/wiki/C_Programming/stdint.h)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-11-04
    • 1970-01-01
    • 2017-08-15
    • 1970-01-01
    • 2016-07-27
    • 2019-12-04
    • 1970-01-01
    相关资源
    最近更新 更多