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

时间:2015-06-13 17:15:39

标签: c random

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

9 个答案:

答案 0 :(得分:539)

var getCameraDistanceFrom = function(camera,x,y,z) { var cameraDistance = new THREE.Vector3(); var target = new THREE.Vector3(x,y,z); cameraDistance.subVectors(camera.position, target); return cameraDistance.length(); }; 定义为返回rand()0之间的整数。

RAND_MAX

可能会溢出。您观察到的可能是整数溢出导致undefined behaviour的结果。

答案 1 :(得分:87)

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

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

结论:避免添加。它没有提供更多随机性&#34;。如果您需要更多位,可以连接值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 + 55 + 6等。

因此,从这方面来看,这也是不好的。

另请注意,生成的结果rand()并非彼此独立,因为它们是由pseudorandom number generator生成的。另请注意,标准未指定计算值的质量或均匀分布。

答案 2 :(得分:35)

这是澄清对this answer

发表评论的问题的答案
  

我加入的原因是为了避免&#39; 0&#39;作为我的代码中的随机数。 rand()+ rand()是我想到的快速肮脏的解决方案。

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

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

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

答案 3 :(得分:3)

基本上rand()会在0RAND_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;
}

答案 4 :(得分:2)

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

答案 5 :(得分:1)

要避免0,请尝试:

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

您需要加入limits.h

答案 6 :(得分:1)

  

我加入的原因是为了避免&#39; 0&#39;作为我的代码中的随机数。 rand()+ rand()是我想到的快速肮脏的解决方案。

一个简单的解决方案(好吧,称之为&#34; Hack&#34;),它永远不会产生零结果,永远不会溢出:

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

这将限制您的最大价值,但如果您不关心这一点,那么这应该适合您。

答案 7 :(得分:0)

thx。我添加的原因是为了避免在代码中使用“ 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; 
    }
}

答案 8 :(得分:-2)

虽然其他所有人都说过可能的溢出很可能是否定的原因,即使你使用无符号整数。真正的问题实际上是使用时间/日期功能作为种子。如果您真正熟悉此功能,您将确切地知道我为什么这么说。正如它真正做的那样是给出自给定日期/时间以来的距离(经过时间)。虽然使用日期/时间功能作为rand()的种子是一种非常普遍的做法,但它确实不是最好的选择。你应该寻找更好的选择,因为有很多关于这个主题的理论,我不可能进入所有这些。你在这个等式中加入了溢出的可能性,这种方法从一开始就注定要失败。

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

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

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