c - 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
每次运行时输出
解决方案
您的代码有两个基本问题,它们结合起来会产生您正在经历的奇怪结果。
几乎任何人都会警告您有关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。
推荐阅读
- r - 如何在 R 中设置正交对比?
- c# - Mandelbrot Set C# 没有做它应该做的事情
- node.js - 跨账户授权和共享 API 资源的最佳实践是什么?
- javascript - 如何搜索多个字符串并在找到一个时停止?
- c# - 在 try/catch 之外使用 try/catch 内的对象
- java - 如何在junit中并行运行testfactory?
- gitlab - 由于 CAfile,ReadTheDocs 无法克隆
- rpc - 如何在不使用任何 API 的情况下在 Java 中设计异步/延迟同步 RPC?
- c++ - 从函数返回后,什么会导致 C++ 对象的类型信息发生变化?
- ios - Xamarin iOS VPN 应用程序扩展未启动数据包隧道提供程序类