C语言的随机函数 rand(), 理论上会在[1, RAND_MAX]之间生成均匀的随机数。但通常情况下,我们需要一个范围很小的随机数(例如随机化快排)。

多数人的做法(包括之前的我),直接采用 rand() % N 的做法。但根据 C-faq 13.15 的介绍,这并不是一个好方法。相比之下,提供了另外两种方案:

  1. (int)((double)rand() / ((double)RAND_MAX + 1) * N)
  2. rand() / (RAND_MAX / N + 1)

为了验证哪种方式生成的随机数更均匀,我就打算做一个小小的测试。

实验步骤:

  1. 使用rand() 生成 10^7 个随机数。
  2. 对于每个随机数,分别带入3个函数中来生成随机数,并将结果输出到文件中。
  3. 使用R语言,利用 hist(x, breaks=range) 来生成图像(感谢SHLUG上的朋友们)。

注:使用 rand()%N 方案生成的随机数被存储到了向量 r0 中,另外两种被存储到了 r1 和 r2 中。经过测试, r1 与 r2 绝大多数元素都是一样的。所以,接下来我仅对比 r0 和 r1

我把范围设到了5000,最后统计的结果让我很震惊:r0 和 r1 的频率分布都很均匀

a0 a1

考虑到可能由于数据量太大抹掉了一些区别,我将数据范围缩减至 10^4

b0 b1

依旧看不到明显的区别。 我将随机数范围缩减到 [0,100)

c0 c1

直观上看,仍然没有本质上的区别。 在 C-faq 13.18 上提到,使用 rand() % N 会让随机数具有周期性。但是我将范围设置为2,也没有找到明显的周期。

由此看来,rand() % N 并没有 显著 的缺陷。但为什么除了 C-FAQ,C语言的科学与艺术 也这么说呢?看来,找寻答案的道路还很漫长啊。

折腾这么久得到这样的一个结论,实在是太让人沮丧了。。。