我有一个问题,我想使用 rand()
来获得 0 到 6 之间的随机数,但每次运行它总是给我 4,即使我调用 srand (时间(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()
接口(interface)的使用。实际上,Mac OS 联机帮助页本身以警告开头:
$ man rand
NAME
rand, srand, sranddev, rand_r -- bad random number generator
是的,这是一个糟糕的随机数生成器。糟糕的随机数生成器可能难以播种,还有其他问题。
但说到播种,这里还有另一个问题,可能讨论得较少但仍然很重要:
Do not use time(NULL)
to seed your random number generator .
链接的答案对此进行了更详细的介绍,但基本问题很简单:time(NULL)
的值很少更改(如果频繁以纳秒为单位),并且不会更改当它改变时很多。因此,您不仅依赖程序不经常运行(或至少每秒运行一次),而且还依赖随机数生成器从略有不同的种子中产生截然不同的值。也许一个好的随机数生成器可以做到这一点,但我们已经确定 rand()
是一个糟糕的随机数生成器。
好的,这很笼统。具体问题有点有趣,至少出于学术目的(学术,因为实际的解决方案总是“使用更好的随机数生成器并用一个好的随机种子来播种它”)。这里的确切问题是您使用的是 rand() % 7
。
这是个问题,因为 Mac OS/FreeBSD 的 rand()
实现是将种子乘以 7 的倍数。因为该乘积是模 232(不是7的倍数),缓慢递增种子产生的第一个随机数的模7值最终会发生变化,但必须等到溢出量发生变化。
这是一个 link to the code .本质就在这三行:
hi = *ctx / 127773;
lo = *ctx % 127773;
x = 16807 * lo - 2836 * hi;
根据评论,“计算 [s] x = (7^5 * x) mod (2^31 - 1) 而不会溢出 31 位。” x
是最终返回的值(模 232),也是下一个种子。 *ctx
是当前种子。
16807,正如评论所说,75,显然可以被 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 秒内发生一次变化,大约一天半。因此,如果您每天运行一次程序,您会注意到第一个随机数有时与前一天相同,有时则少一个。但是你运行它的频率比这高得多,即使你在运行之间等待几秒钟,所以你每次都会看到相同的第一个随机数。最终它会下降,然后您会看到一系列 3 而不是 4。