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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30821406/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 11:59:18  来源:igfitidea点击:

Why does rand() + rand() produce negative numbers?

crandom

提问by badmad

I observed that rand()library function when it is called just once within a loop, it almost always produces positive numbers.

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

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

But when I add two rand()calls, the numbers generated now have more negative numbers.

但是当我添加两个rand()调用时,生成的数字现在有更多的负数。

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

Can someone explain why I am seeing negative numbers in the second case?

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

PS: I initialize the seed before the loop as srand(time(NULL)).

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

回答by P.P

rand()is defined to return an integer between 0and RAND_MAX.

rand()被定义为返回一个介于0和之间的整数RAND_MAX

rand() + rand()

could overflow. What you observe is likely a result of undefined behaviourcaused by integer overflow.

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

回答by too honest for this site

The problem is the addition. rand()returns an intvalue of 0...RAND_MAX. So, if you add two of them, you will get up to RAND_MAX * 2. If that exceeds INT_MAX, the result of the addition overflows the valid range an intcan hold. Overflow of signed values is undefined behaviour and may lead to your keyboard talking to you in foreign tongues.

问题是加法。rand()返回一个int0...RAND_MAX。因此,如果您添加其中两个,您将达到RAND_MAX * 2. 如果超过INT_MAX,加法的结果会溢出int可以容纳的有效范围。有符号值的溢出是未定义的行为,可能会导致您的键盘用外语与您交谈。

As there is no gain here in adding two random results, the simple idea is to just not do it. Alternatively you can cast each result to unsigned intbefore the addition if that can hold the sum. Or use a larger type. Note that longis not necessarily wider than int, the same applies to long longif intis at least 64 bits!

由于添加两个随机结果没有任何好处,所以简单的想法就是不要这样做。或者,unsigned int如果可以容纳总和,您可以将每个结果转换为加法之前。或者使用更大的类型。请注意,long不一定比 更宽int,同样适用于long longifint至少为 64 位!

Conclusion: Just avoid the addition. It does not provide more "randomness". If you need more bits, you might concatenate the values sum = a + b * (RAND_MAX + 1), but that also likely requires a larger data type than int.

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

As your stated reason is to avoid a zero-result: That cannot be avoided by adding the results of two rand()calls, as both can be zero. Instead, you can just increment. If RAND_MAX == INT_MAX, this cannot be done in int. However, (unsigned int)rand() + 1will do very, very likely. Likely (not definitively), because it does require UINT_MAX > INT_MAX, which is true on all implementations I'm aware of (which covers quite some embedded architectures, DSPs and all desktop, mobile and server platforms of the past 30 years).

正如您所说的原因是避免零结果:通过将两个rand()调用的结果相加是无法避免的,因为两者都可以为零。相反,您可以增加。如果RAND_MAX == INT_MAX,这不能在 中完成int。但是,(unsigned int)rand() + 1很有可能会这样做。可能(不是绝对),因为它确实需要UINT_MAX > INT_MAX,这在我所知道的所有实现中都是正确的(涵盖了相当多的嵌入式架构、DSP 以及过去 30 年的所有桌面、移动和服务器平台)。

Warning:

警告:

Although already sprinkled in comments here, please note that adding two random values does notget a uniform distribution, but a triangular distribution like rolling two dice: to get 12(two dice) both dice have to show 6. for 11there are already two possible variants: 6 + 5or 5 + 6, etc.

虽然已经评论洒在这里,请注意添加两个随机值并没有得到均匀分布,而是一个三角形分布状滚动两个骰子:让12(两个骰子)两个骰子必须显示6。因为11已经有两种可能的变体:6 + 5or5 + 6等。

So, the addition is also bad from this aspect.

所以,从这方面来说,加法也是不好的。

Also note that the results rand()generates are not independent of each other, as they are generated by a pseudorandom number generator. Note also that the standard does not specify the quality or uniform distribution of the calculated values.

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

回答by David Hammen

This is an answer to a clarification of the question made in comment to this answer,

这是对该答案的评论中对该问题的澄清的答案

the reason i was adding was to avoid '0' as the random number in my code. rand()+rand() was the quick dirty solution which readily came to my mind.

我添加的原因是为了避免在我的代码中将“0”作为随机数。rand()+rand() 是我很容易想到的快速肮脏的解决方案。

The problem was to avoid 0. There are (at least) two problems with the proposed solution. One is, as the other answers indicate, that rand()+rand()can invoke undefined behavior. Best advice is to never invoke undefined behavior. Another issue is there's no guarantee that rand()won't produce 0 twice in a row.

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

The following rejects zero, avoids undefined behavior, and in the vast majority of cases will be faster than two calls to rand():

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

int rnum;
for (rnum = rand(); rnum == 0; rnum = rand()) {}
// or do rnum = rand(); while (rnum == 0);

回答by Khaled.K

Basically rand()produce numbers between 0and RAND_MAX, and 2 RAND_MAX > INT_MAXin your case.

基本上rand()0和之间产生数字RAND_MAX2 RAND_MAX > INT_MAX在你的情况下。

You can modulus with the max value of your data-type to prevent overflow. This ofcourse will disrupt the distribution of the random numbers, but randis just a way to get quick random numbers.

您可以使用数据类型的最大值进行模数以防止溢出。这当然会破坏随机数的分布,但这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;
}

回答by Jibin Mathew

May be you could try rather a tricky approach by ensuring that the value returned by sum of 2 rand() never exceeds the value of RAND_MAX. A possible approach could be sum = rand()/2 + rand()/2; This would ensure that for a 16 bit compiler with RAND_MAX value of 32767 even if both rand happens to return 32767, even then (32767/2 = 16383) 16383+16383 = 32766, thus would not result in negative sum.

也许您可以尝试一种相当棘手的方法,确保 2 rand() 之和返回的值永远不会超过 RAND_MAX 的值。一种可能的方法是 sum = rand()/2 + rand()/2; 这将确保对于 RAND_MAX 值为 32767 的 16 位编译器,即使两个 rand 恰好返回 32767,即使那样 (32767/2 = 16383) 16383+16383 = 32766,也不会导致负和。

回答by Doni

To avoid 0, try this:

为了避免 0,试试这个:

int rnumb = rand()%(INT_MAX-1)+1;

You need to include limits.h.

您需要包括limits.h.

回答by Kevin Fegan

the reason i was adding was to avoid '0' as the random number in my code. rand()+rand() was the quick dirty solution which readily came to my mind.

我添加的原因是为了避免在我的代码中将“0”作为随机数。rand()+rand() 是我很容易想到的快速肮脏的解决方案。

A simple solution (okay, call it a "Hack") which never produces a zero result and will never overflow is:

一个永远不会产生零结果并且永远不会溢出的简单解决方案(好吧,称之为“黑客”)是:

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

This will limit your maximum value, but if you don't care about that, then this should work fine for you.

这将限制您的最大值,但如果您不关心这一点,那么这对您来说应该可以正常工作。

回答by Mark Krug

While what everyone else has said about the likely overflow could very well be the cause of the negative, even when you use unsigned integers. The real problem is actually using time/date functionality as the seed. If you have truly become familiar with this functionality you will know exactly why I say this. As what it really does is give a distance (elapsed time) since a given date/time. While the use of the date/time functionality as the seed to a rand(), is a very common practice, it really is not the best option. You should search better alternatives, as there are many theories on the topic and I could not possibly go into all of them. You add into this equation the possibility of overflow and this approach was doomed from the beginning.

虽然其他人对可能溢出的评论很可能是负面的原因,即使您使用无符号整数也是如此。真正的问题实际上是使用时间/日期功能作为种子。如果你真正熟悉了这个功能,你就会明白我为什么这么说。因为它真正做的是给出自给定日期/时间以来的距离(经过的时间)。虽然使用日期/时间功能作为 rand() 的种子是一种非常常见的做法,但它确实不是最佳选择。你应该寻找更好的替代方案,因为关于这个主题有很多理论,我不可能全部都讲。您将溢出的可能性添加到这个等式中,这种方法从一开始就注定要失败。

Those that posted the rand()+1 are using the solution that most use in order to guarantee that they do not get a negative number. But, that approach is really not the best way either.

那些发布 rand()+1 的人正在使用大多数使用的解决方案,以保证他们不会得到负数。但是,这种方法也确实不是最好的方法。

The best thing you can do is take the extra time to write and use proper exception handling, and only add to the rand() number if and/or when you end up with a zero result. And, to deal with negative numbers properly. The rand() functionality is not perfect, and therefore needs to be used in conjunction with exception handling to ensure that you end up with the desired result.

你能做的最好的事情是花额外的时间来编写和使用适当的异常处理,并且只有在和/或当你最终得到零结果时才添加到 rand() 数字。并且,正确处理负数。rand() 功能并不完美,因此需要与异常处理结合使用以确保最终得到所需的结果。

Taking the extra time and effort to investigate, study, and properly implement the rand() functionality is well worth the time and effort. Just my two cents. Good luck in your endeavors...

花费额外的时间和精力来调查、研究和正确实现 rand() 功能是非常值得的。只有我的两分钱。祝你的努力好运...