【问题标题】:clang - poor rand() implementationclang - 糟糕的 rand() 实现
【发布时间】:2021-02-13 14:10:29
【问题描述】:

我做了一个Pong 游戏,我必须随机投球。为此,我使用了内置的随机数生成器,并在 Ubuntu/GCC 中获得了不错的结果。将代码移植到 Mac 并编译时,球只会向一个方向投射,这意味着 clang 中的 rand() 函数没有按预期工作。

我已经恢复到自定义数字生成器,但这是游戏中原始有问题的代码:

srand(time(NULL));
float ang = ((float)rand())/RAND_MAX * 120 - 60;
int side = (int)(((float)rand())/RAND_MAX * 2);

经过进一步检查,我创建了以下测试程序并在两个平台上编译并运行它。

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

int main(int argc, char** argv) {
    while (1) {
        int now = time(NULL);
        printf("Time now is %d\n", now);
        srand(now);
        int random = rand();
        printf("Random number generated is %d\n", random);
        sleep(1);
    }
}

结果(根据RAND_MAX(两个系统上的2147483647)制表和计算)如下:

结果不言自明。虽然 clang 版本存在随机性,但与 RAND_MAX 的值相比,随机性很差,标准差为 0,而 gcc 的标准差为 0.328。

rand() 在 clang 上的这种不良行为的原因是什么?我还想在头文件中看到这个函数的参考实现。这似乎是不标准的,而且我一定不是唯一一个在使用这种方法将程序移植到 Mac 时遇到问题的人。在使用/依赖随机数生成器时,还有哪些其他设计原则是好的做法?

【问题讨论】:

  • 根据 Mac 自己的说法,他们对 rand() 的实现很糟糕:developer.apple.com/library/archive/documentation/System/… ... 并被 random() 淘汰
  • 我很想看看这个 srand() 和 rand() 实现的源代码。
  • 我想说最重要的原则是选择一个经得起分析并且今天仍然受到尊重的随机数生成器。 rand() 唯一追求的就是便携性。请注意,没有要求 rand() 实现不佳,只是似乎经常如此。
  • 在调用srand() 后多次调用rand() 会发生什么?它仍然只是略有不同吗?
  • 不怪clang,不夸gcc;随机数生成器是系统标准库的一部分,而不是编译器。相反,它是 MacOS 与 glibc。

标签: c gcc random numbers clang


【解决方案1】:

行为不正确的原因:

所以我不知道苹果的 rand 的 clang 实现的确切实现,但我知道它是作为 LCG 或线性同余生成器完成的。它的工作方式基本上是一个公式:

在公式中, 将是 RAND_MAX, 和 是编译器定义的一些常量。 Apple 的 Clang 分别使用了 168070

rand() 的返回值为 ,rand() 的最后一个返回值或您在 srand() 中输入的值将是 。

如果您只想要一个数字序列,有点随机,这很有效,因为它最终会返回 0RAND_MAX 之间的所有数字,而人类没有明显的模式。

但是,通过一些计算,您可以很容易地发现,如果您只是将一个连续的数字序列放在该公式中的 的位置(这就是您所做的),就会出现一些问题。每次调用rand() 之间的差异将完全等于,除非 实际上接近RAND_MAX


正确使用方法:

通过rand()函数获取随机数的正确方法是在开始时只调用一次srand(),并且在循环中只使用rand()

或者,您可以寻找许多其他随机生成公式,您可能可以在这里找到一些:How to generate a random int in C?