您的代码存在两个基本问题,这两个问题结合起来会产生您所遇到的奇怪结果。
几乎任何人都会警告您有关使用rand()
界面。事实上,Mac OS 手册页本身就以警告开头:
$ man rand
NAME
rand, srand, sranddev, rand_r -- bad random number generator
是的,这是一个糟糕的随机数生成器。除其他问题外,不良随机数生成器可能难以播种。
但说到播种,还有另一个问题,也许讨论较少,但仍然很重要:不使用time(NULL)为您的随机数生成器播种 https://stackoverflow.com/a/46879475/1566221.
链接的答案对此进行了更详细的说明,但基本问题很简单:time(NULL)
变化很少(如果频繁以纳秒为单位),并且变化时变化不大。因此,您不仅依赖于程序不经常运行(或至少每秒少于一次),而且还依赖于随机数生成器从稍微不同的种子生成完全不同的值。也许一个好的随机数生成器可以做到这一点,但我们已经确定了rand()
是一个糟糕的随机数生成器。
好吧,这一切都很一般。具体问题有点有趣,至少出于学术目的(学术,因为实际的解决方案总是“使用更好的随机数生成器and用一个好的随机种子播种它”)。这里的确切问题是你正在使用rand() % 7
.
That's a problem because what the Mac OS / FreeBSD implementation of rand()
does is to multiply the seed by a multiple of 7. Because that product is reduced modulo 232 (which is not a multiple of 7), the value modulo 7 of the first random number produced by slowly incrementing seeds will eventually change, but it will have to wait until the amount of the overflow changes.
这是一个链接到代码 https://opensource.apple.com/source/Libc/Libc-1439.40.11/stdlib/FreeBSD/rand.c.auto.html。本质就在这三行:
hi = *ctx / 127773;
lo = *ctx % 127773;
x = 16807 * lo - 2836 * hi;
which, according to a comment, "compute[s] x = (7^5 * x) mod (2^31 - 1) without overflowing 31 bits." x
is the value which will eventually be returned (modulo 232) and it is also the next seed. *ctx
is the current seed.
16807 is, as the comment says, 75, which is obviously divisible by 7. And 2836 mod 7 is 1. So by the rules of modular arithmetic:
x mod 7 = (16807 * lo) mod 7 - (2836 * hi) mod 7
= 0 - hi mod 7
该值仅取决于hi
,即seed / 127773
. So hi
每 127773 个刻度变化一次。由于结果time(NULL)
以秒为单位,这是 127773 秒的一个变化,大约是一天半。因此,如果您每天运行一次程序,您会注意到第一个随机数有时与前一天相同,有时则少一个。但你运行它的频率比这要高得多,即使你在运行之间等待几秒钟,所以你每次都会看到相同的第一个随机数。最终它会滴答作响,然后你会看到一系列的 3 而不是 4。