[这是我回复一位询问 FAQ 列表中有关 问题 13.16 答案中一些表达方式的通信者的内容。我已对文本稍作编辑,以适用于此网页。]

日期:2000 年 2 月 23 日星期三 07:08:14 -0800 (PST)
Message-Id: <200002231508.HAA07282@mail.eskimo.com>
主题:Re: C FAQ 错误?

这里的水比预期的要深。我最初对您的留言感到有些担忧,因为在我翻箱倒柜找出一些旧的随机数测试平台并掸去灰尘后,我发现 FAQ 列表中的这个表达方式

	rand() / (RAND_MAX/N + 1)
的行为不如我预期的那么好。当我随后尝试重新推导合适的表达式时,我的第一次尝试得到了
	rand() / ((RAND_MAX + 1) / N)
这正是您建议的。但事实证明,后一个表达式在一般情况下并不能正常工作。假设RAND_MAX为 21,而N为 10。(碰巧,这是我尝试的第一个例子。)那么((RAND_MAX + 1) / N)就等于 2。当rand()返回 20 或 21 时,该表达式的值为 10,这不在 [0,N).

我想仔细研究您的……推导过程,因为我认为它基本上是正确的,对于“|RAND_MAX+1| 是 |N| 的精确倍数”的情况。我怀疑,然而,问题的症结在于您假设如果RAND_MAX+1 不是 ... 的倍数,N那么同样的逻辑也适用。您必须确保当中间的商丢弃一个非零余数时,结果会以正确的方式落入范围,而对于这个表达式,它并没有做到。

不幸的是,我一时想不起我最初是如何推导出那个表达式的

	rand() / (RAND_MAX/N + 1)
我同意你对它与(更简洁的)浮点数版本的不同之处感到不安
	rand() / (RAND_MAX + 1.0) * N
我确实记得我曾非常彻底地测试过 FAQ 列表中的这些表达式,并且我找到了一篇我很久以前发布的文章,其中详细讨论了它们,并且表明我在最初的推导上花了很多时间。(它还表明,我当时对那个表达式也并不完全满意。)现在我再读那篇文章,感觉远不如我希望的那样清晰,而且我几乎要对如何解释
	rand() / (RAND_MAX/N + 1)
有效工作而一筹莫展了,直到我发现这个表达式本质上和我解释的在练习 8 中的那个是同一个,在 http://www.eskimo.com/~scs/cclass/asgn.beg/PS3a.html,这值得一读。(不过我必须承认,我无法想象我在一个声称是初级编程的课程的第三周开始扔出那么多细节时到底在想什么。)但回到RAND_MAX=21, N10 的情况,事实证明,将rand()除以 3,这样我们就根本不会发出 8 或 9,这是我们能做到的最好的;我们所看到的只是这些表达式在RAND_MAX+1 既不是 ... 的倍数,也不是远大于 ... 的时候开始失效的几种方式之一。N.

为了我个人参考,这里是另一种推导表达式的方法rand() / ((RAND_MAX + 1) / N),尽管那个表达式并不奏效。我们想将rand的输出除以某个整数 y,使得结果总是在范围 0 <=rand()/y <N。现在当 rand 返回RAND_MAX时,结果RAND_MAX/y 应该刚好小于N;一种实现方法是,如果 (RAND_MAX+1)/y 等于N。所以如果我们设 (RAND_MAX+1)/y =N,我们可以轻松地解出 y = (RAND_MAX+1)/N.

但等等!如果我们想要RAND_MAX/y 刚好小于N,另一种实现方法是在分母中减去 1。所以如果RAND_MAX/(y-1) =N,那么我们有 y =RAND_MAX/N+ 1,这一定是 FAQ 列表中的表达式的来源,毕竟。