我观察到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))。


当前回答

也许你可以尝试一种比较棘手的方法,确保2 rand()的sum返回的值永远不会超过RAND_MAX的值。一种可能的方法是sum = rand()/2 + rand()/2;这将确保对于RAND_MAX值为32767的16位编译器,即使两个rand都返回32767,即使(32767/2 = 16383)16383+16383 = 32766,也不会产生负和。

其他回答

问题是加法。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()是我脑海中迅速出现的肮脏解决方案

对我来说,这听起来像是一个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; 
    }
}

我添加的原因是为了避免在我的代码中使用“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()是我脑海中迅速出现的肮脏解决方案。

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

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

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

要避免0,试试这个:

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

你需要包括限制。h。