我观察到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。或者使用大一点的字体。注意long并不一定比int更宽,如果int至少是64位,这同样适用于long long !

结论:避免加法。它没有提供更多的“随机性”。如果需要更多的位,可以将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() + 1 will 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).

警告:

尽管在这里已经有一些评论,但请注意,将两个随机值相加并不能得到均匀分布,而是一个三角形分布,就像滚动两个骰子一样:要得到12(两个骰子),两个骰子都必须显示6。对于11,已经有两种可能的变体:6 + 5或5 + 6,等等。

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

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

这是对对这个回答的评论中提出的问题的澄清的回答,

我添加的原因是为了避免在我的代码中使用“0”作为随机数。Rand ()+ Rand()是我脑海中迅速出现的肮脏解决方案。

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

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

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

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()+1的人正在使用最常用的解决方案,以确保他们不会得到负数。但是,这种方法也不是最好的方法。

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

花费额外的时间和精力来调查、研究和正确地实现rand()功能是非常值得的。这只是我的个人意见。祝你好运……

也许你可以尝试一种比较棘手的方法,确保2 rand()的sum返回的值永远不会超过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;

你需要包括限制。h。

基本上rand()生成0和RAND_MAX之间的数字,在您的情况下生成2 RAND_MAX > INT_MAX。

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

我添加的原因是为了避免在我的代码中使用“0”作为随机数。Rand ()+ Rand()是我脑海中迅速出现的肮脏解决方案。

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

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

这将限制最大值,但如果您不关心这一点,那么这应该可以为您工作。

谢谢。我添加的原因是为了避免在我的代码中使用“0”作为随机数。Rand ()+ Rand()是我脑海中迅速出现的肮脏解决方案

对我来说,这听起来像是一个XY问题,为了从rand()中不得到0,你调用rand()两次,使程序变慢,有一个新的挫折,得到0的可能性仍然存在。

另一个解决方案是使用uniform_int_distribution,它在定义的间隔内创建一个随机且均匀分布的数字:

https://wandbox.org/permlink/QKIHG4ghwJf1b7ZN

#include <random>
#include <array>
#include <iostream>
 
int main()
{
    const int MAX_VALUE=50;
    const int MIN_VALUE=1;
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> distrib(MIN_VALUE, MAX_VALUE);
    std::array<int,MAX_VALUE-MIN_VALUE> weight={0};

    for(int i=0; i<50000; i++) {
        weight[distrib(gen)-MIN_VALUE]++;
    }
    
    for(int i=0;i<(int)weight.size();i++) {
        std::cout << "value: " << MIN_VALUE+i << " times: " << weight[i] << std::endl; 
    }
}