为什么rand()+ rand()产生负数?

bad*_*mad 302 c random

我观察到rand()库函数在循环中只调用一次,它几乎总是产生正数.

for (i = 0; i < 100; i++) {
    printf("%d\n", rand());
}
Run Code Online (Sandbox Code Playgroud)

但是当我添加两个rand()电话时,现在生成的数字会有更多的负数.

for (i = 0; i < 100; i++) {
    printf("%d = %d\n", rand(), (rand() + rand()));
}
Run Code Online (Sandbox Code Playgroud)

有人可以解释为什么我在第二种情况下看到负数吗?

PS:我在循环之前初始化种子为srand(time(NULL)).

P.P*_*.P. 541

rand()被定义为在0和之间返回一个整数RAND_MAX.

rand() + rand()
Run Code Online (Sandbox Code Playgroud)

可能会溢出 您观察到的可能是整数溢出导致的未定义行为的结果.

  • 这是C标准中与有符号整数溢出相关的引用:**如果在表达式求值过程中出现异常情况(即,如果结果未在数学上定义,或者在其类型的可表示值范围内没有),行为未定义.** (11认同)
  • @JakubArnold:每种语言指定溢出行为的方式有何不同?例如,Python没有(好的,可用内存),因为int只是增长. (4认同)
  • @EvanCarslake稍微偏离C编译器使用标准的问题,对于有符号整数,如果他们知道'b> 0`,他们可以假设'a + b> a`.他们还可以假设如果后来执行的语句"a + 5"则当前值低于"INT_MAX - 5".因此,即使在没有陷阱程序的2的补码处理器/解释器上也可能不会表现为`int`s是没有陷阱的2的补码. (3认同)
  • @Olaf这取决于语言如何决定表示有符号整数。*Java* 没有检测整数溢出的机制(直到 Java 8)并将其定义为环绕,*Go* 仅使用 2 的补码表示并将其定义为对有符号整数溢出合法。C 显然支持多于 2 的补码。 (2认同)
  • @EvanCarslake不,这不是一种普遍的行为.你说的是2的补码表示.但C语言也允许其他表示.C语言规范说有符号整数溢出是*undefined*.所以一般来说,没有程序应该依赖这种行为,并且需要仔细编码以不导致有符号整数溢出.但是这不适用于无符号整数,因为它们将以明确定义的(简化模2)方式"环绕".[继续]... (2认同)

too*_*ite 88

问题是增加.rand()返回int0...RAND_MAX.所以,如果你添加其中两个,你就可以了RAND_MAX * 2.如果超过INT_MAX,则添加的结果溢出int可以容纳的有效范围.签名值溢出是未定义的行为,可能导致您的键盘用外语说话.

由于添加两个随机结果没有增益,简单的想法就是不这样做.或者,unsigned int如果可以保存总和,则可以在添加之前将每个结果转换为.或者使用更大的类型.注意,long不一定要宽int,同样适用于long longif int至少是64位!

结论:避免添加.它没有提供更多的"随机性".如果需要更多位,可以连接值sum = a + b * (RAND_MAX + 1),但这也可能需要更大的数据类型int.

正如你所陈述的原因是为了避免零结果:通过添加两个rand()调用的结果无法避免,因为两者都可以为零.相反,你可以增加.如果RAND_MAX == INT_MAX,这不能做到int.但是,(unsigned int)rand() + 1非常非常可能.可能(不是最终),因为它确实需要UINT_MAX > INT_MAX,这在我所知道的所有实现中都是如此(其中涵盖了过去30年中的一些嵌入式架构,DSP和所有桌面,移动和服务器平台).

警告:

虽然这里已经有评论,但请注意,添加两个随机值并不能得到均匀分布,而是像滚动两个骰子一样的三角形分布:得到12(两个骰子)两个骰子都必须显示6.因为11已经有两种可能的变体:6 + 5或者5 + 6等等

因此,从这方面来说,增加也是不好的.

还要注意,rand()生成的结果不是彼此独立的,因为它们是由伪随机数生成器生成的.另请注意,标准未指定计算值的质量或均匀分布.

  • @badmad:那么如果两个调用都返回0怎么办? (14认同)
  • 要避免0,一个简单的"虽然结果为0,重新滚动"? (6认同)
  • @badmad:我只是想知道标准是否保证了'UINT_MAX> INT_MAX!= false`.(很可能,但不确定是否需要).如果是这样,您可以只生成一个结果并递增(按此顺序!). (3认同)
  • 当您需要非均匀分布时,可以添加多个随机数:http://stackoverflow.com/questions/30492259/get-a-random-number-focused-on-center (3认同)
  • 添加它们不仅是避免0的坏方法,而且还会导致不均匀的分布.你会得到像滚动骰子的结果一样的分布:7是2或12的6倍. (2认同)

Dav*_*men 36

这是澄清对此答案发表评论的问题的答案,

我添加的原因是为了避免'0'作为我的代码中的随机数.rand()+ rand()是我想到的快速肮脏的解决方案.

问题是避免0.建议的解决方案存在(至少)两个问题.一个是,正如其他答案所示,rand()+rand()可以调用未定义的行为.最好的建议是永远不要调用未定义的行为.另一个问题是不能保证rand()不会连续两次产生0.

以下拒绝零,避免未定义的行为,并且在绝大多数情况下将比两次调用更快rand():

int rnum;
for (rnum = rand(); rnum == 0; rnum = rand()) {}
// or do rnum = rand(); while (rnum == 0);
Run Code Online (Sandbox Code Playgroud)

  • 如果RAND_MAX == MAX_INT,则rand()+ 1将以与rand()为0的值完全相同的概率溢出,这使得此解决方案完全没有意义.如果你愿意冒险并忽略溢出的可能性,你也可以使用rand()并忽略它返回0的可能性. (10认同)
  • 那么`rand()+ 1`怎么样? (8认同)
  • @askvictor那可能会溢出(虽然不太可能). (3认同)
  • @gerrit - 取决于MAX_INT和RAND_MAX (3认同)
  • @gerrit,如果它们不相同,我会感到惊讶,但我想这是一个适合学生的地方:) (3认同)
  • @KevinFegan 如果实验重复得足够频繁,任何 $p&gt;0$ 的事件最终都会发生。这两个陈述是相同的。 (2认同)