为什么rand()+ rand()产生负数?
我观察到rand()
库函数在循环中只被调用一次,它几乎总是产生正数。
for (i = 0; i < 100; i++) { printf("%d\n", rand()); }
但是当我添加两个rand()
调用时,生成的数字现在有更多的负数。
for (i = 0; i < 100; i++) { printf("%d = %d\n", rand(), (rand() + rand())); }
有人能解释为什么我在第二种情况下看到负数?
PS:我在循环之前初始化种子srand(time(NULL))
。
rand()
定义为返回0
到RAND_MAX
之间的整数。
rand() + rand()
可能溢出。 你观察到的可能是由整数溢出引起的未定义行为的结果。
问题是增加。 rand()
返回一个int
值0...RAND_MAX
。 所以,如果你添加两个,你将达到RAND_MAX * 2
。 如果超过了INT_MAX
,则加法结果会溢出int
可以保存的有效范围。 有符号值的溢出是未定义的行为,并可能导致您的键盘以外语方式与您交谈。
由于这里没有增加两个随机结果,所以简单的想法就是不去做。 或者,如果可以保存总和,则可以在添加前将每个结果转换为unsigned int
。 或者使用更大的types。 请注意, long
不一定比int
更宽,如果int
至less为64位, int
也是一样。
结论:只要避免添加。 它不提供更多的“随机性”。 如果您需要更多位,则可以连接值sum = a + b * (RAND_MAX + 1)
,但是这也可能需要比int
更大的数据types。
正如你所说的原因是为了避免零结果:通过添加两个rand()
调用的结果是无法避免的,因为两者都可以是零。 相反,你可以增加。 如果RAND_MAX == INT_MAX
,则不能在int
完成。 但是, (unsigned int)rand() + 1
会非常非常可能。 可能(而不是确定地),因为它确实需要UINT_MAX > INT_MAX
,我不确定是否有保证,但是对于我实际上知道的所有实现(而不仅仅是x86 / 64或ARM)有效。
警告:
虽然已经在这里注释了,但是请注意添加两个随机值并不是一个统一的分布,而是一个三angular形的分布,如滚动两个骰子:得到12
(两个骰子),两个骰子都必须显示6
。 对于11
,已经有两种可能的变体: 6 + 5
或5 + 6
等
所以,从这方面来说也是不好的。
还要注意, rand()
生成的结果并不相互独立,因为它们是由伪随机数生成器生成的 。 还要注意,该标准没有规定计算值的质量或均匀分布。
这是澄清对这个答案发表评论的问题的答案 ,
我添加的原因是为了避免“0”作为我的代码中的随机数字。 兰特()+兰特()是快速肮脏的解决scheme,很容易出现在我的脑海里。
问题是避免0。提出的解决scheme存在(至less)两个问题。 正如其他答案指出的那样, rand()+rand()
可以调用未定义的行为。 最好的build议是永远不要调用未定义的行为。 另一个问题是不能保证rand()
不会连续两次产生0。
以下拒绝零,避免未定义的行为,在绝大多数情况下,将比两个调用rand()
更快:
int rnum; for (rnum = rand(); rnum == 0; rnum = rand()) {} // or do rnum = rand(); while (rnum == 0);
基本上rand()
产生0
到RAND_MAX
之间的数字, 2 RAND_MAX > INT_MAX
在你的情况下。
您可以使用数据types的最大值进行模数化以防止溢出。 这当然会扰乱随机数的分布,但rand
只是一种获得快速随机数的方法。
#include <stdio.h> #include <limits.h> int main(void) { int i=0; for (i=0; i<100; i++) printf(" %d : %d \n", rand(), ((rand() % (INT_MAX/2))+(rand() % (INT_MAX/2)))); for (i=0; i<100; i++) printf(" %d : %ld \n", rand(), ((rand() % (LONG_MAX/2))+(rand() % (LONG_MAX/2)))); return 0; }
可能是你可以尝试一个相当棘手的方法,确保由2 rand()总和返回的值永远不会超过RAND_MAX的值。 一个可能的方法可能是sum = rand()/ 2 + rand()/ 2; 这将确保对于RAND_MAX值为32767的16位编译器,即使这两个rand恰好返回32767,即使(32767/2 = 16383)16383 + 16383 = 32766,也不会导致负和。
为了避免0,试试这个:
int rnumb = rand()%(INT_MAX-1)+1;
你需要包含limits.h。
对不起,我的英语不好。
我添加的原因是为了避免“0”作为我的代码中的随机数。 兰特()+兰特()是快速肮脏的解决scheme,很容易出现在我的脑海里。
一个简单的解决scheme(好吧,称之为“Hack”)永远不会产生零结果,永远不会溢出:
x=(rand()/2)+1 // using divide -or- x=(rand()>>1)+1 // using shift which may be faster // compiler optimization may use shift in both cases
这会限制你的最大值,但是如果你不关心这个,那么这对你来说应该是正常的。
尽pipe其他人都说过可能的溢出很可能是负面的原因,即使使用无符号整数。 真正的问题实际上是使用时间/datefunction作为种子。 如果你真的熟悉了这个function,你就会知道我为什么这么说。 正如它真正做的是给一个距离(经过的时间)从一个给定的date/时间。 虽然使用date/时间function作为rand()的种子是非常普遍的做法,但它并不是最好的select。 你应该寻找更好的select,因为关于这个主题有许多理论,我不可能进入所有的理论。 你在这个等式中增加了溢出的可能性,这种方法从一开始就注定了。
那些发布rand()+ 1的人正在使用最常用的解决scheme,以保证他们不会得到负数。 但是,这种方法也不是最好的方法。
你可以做的最好的事情是花费额外的时间来写和使用适当的exception处理,并且如果和/或当最终得到零结果时只添加到rand()数。 而且,正确地处理负数。 rand()函数并不完美,因此需要结合exception处理来使用,以确保您获得期望的结果。
花费额外的时间和精力去研究,学习和正确地实现rand()函数是非常值得花时间和精力的。 只是我的两分钱。 祝你好运