介于0和1之间的rand()


71

所以下面的代码使0 <r <1

r = ((double) rand() / (RAND_MAX))

为什么 r = ((double) rand() / (RAND_MAX + 1))使-1 <r <0?

难道不应该在RAND_MAX中加1使得r <2?

编辑:我收到警告:表达式中的整数溢出

在那条线上,所以可能是问题所在。我刚刚做了cout << r << endl,它的确给了我-1和0之间的值


2
尝试1在最后一个之后添加)
Vincenzo Pii 2012年

15
请注意,无论如何,您都应该使用uniform_real_distribution。如果您没有C ++ 11,请使用boost版本
KillianDS 2012年

代数错误。如果r是在零到范围内的随机数m,则比率r/m将在范围内(0,1),但r/(m+1)将在(0, m/(m+1))非范围内(1,2)。如果m非常大(与1相比),则m/(m+1)大约为1,因此您的表达式r = ((double) rand() / (RAND_MAX + 1))将给出大约在(0,1)范围内的随机数-也就是说,如果没有溢出。
飞行

Answers:


85

这完全是特定实现的,但是似乎在您使用的C ++环境中RAND_MAX等于INT_MAX

因此,RAND_MAX + 1显示不确定的(溢出)行为,并变为INT_MIN。当您的初始语句是在(0和之间的随机数INT_MAX)/(INT_MAX)并生成一个值时0 <= r < 1,现在它是在(0和之间的随机数INT_MAX)/(INT_MIN)进行分割,从而生成一个值-1 < r <= 0

为了生成一个随机数1 <= r < 2,您需要

r = ((double) rand() / (RAND_MAX)) + 1

1
你为什么要放在方括号内RAND_MAX
ssc 2014年

1
@ssc强调与OP的代码的区别。这些括号没有技术原因。
疯狂物理学家

该OP对尝试它的推理是错误的,但有这是必要的,在UB可能已经避免加入1.0,而不是1,这将迫使RAND_MAXdouble类型等避免整数溢出。
underscore_d

@ssc因为它是宏。除非您确切知道它的定义方式,否则应始终使用宏来执行此操作。考虑这样的定义#define RAND_MAX 1 + 100上面的表达式的结果是什么?;-)
Valentin Heinitz

@MadPhysicist除非您确切知道,否则宏不是表达式而是单个数字。
Valentin Heinitz

29

rand() / double(RAND_MAX)生成介于0(包括)和1(包括)之间的浮点随机数,但是由于以下原因,这不是一个好方法(因为RAND_MAX通常为32767):

  1. 可以生成的不同随机数的数量太少:32768。如果需要更多不同的随机数,则需要使用不同的方式(下面给出代码示例)
  2. 生成的数字过于粗糙:您可以获得1 / 32768、2 / 32768、3 / 32768,但两者之间却什么也没有。
  3. 随机数生成器引擎的受限状态:生成RAND_MAX随机数后,实现通常会开始重复相同的随机数序列。

由于rand()的上述限制,下面的代码段是生成0(包括)和1(包括)之间的随机数的更好选择(类似于http://en.cppreference.com/w上的示例/ cpp / numeric / random / uniform_real_distribution):

#include <iostream>
#include <random>
#include <chrono>

int main()
{
    std::mt19937_64 rng;
    // initialize the random number generator with time-dependent seed
    uint64_t timeSeed = std::chrono::high_resolution_clock::now().time_since_epoch().count();
    std::seed_seq ss{uint32_t(timeSeed & 0xffffffff), uint32_t(timeSeed>>32)};
    rng.seed(ss);
    // initialize a uniform distribution between 0 and 1
    std::uniform_real_distribution<double> unif(0, 1);
    // ready to generate random numbers
    const int nSimulations = 10;
    for (int i = 0; i < nSimulations; i++)
    {
        double currentRandomNumber = unif(rng);
        std::cout << currentRandomNumber << std::endl;
    }
    return 0;
}

通过替换为unif(0, 1),可以很容易地对其进行修改以生成介于1(含)和2(不含)之间的随机数unif(1, 2)


我的妈呀。我一直在试图找出一个零对一的随机数生成器,以便在while循环中使用它,这实际上可能有效。我尝试了random_device所有可能的方法,但是得到了相同的输出。我不知道0xfffffffff是什么(十六进制代码?),右移到32 ...两者都在我头上。(我刚接触C ++,但是它比Rust,恕我直言要容易得多。)谢谢!
Mark Moretto

18

不可以,因为RAND_MAX通常扩展为MAX_INT。因此,添加一个(显然)将其放在MIN_INT位置(尽管据我所知,这应该是未定义的行为),因此符号反转了。

要获得所需的内容,您需要将+1移动到计算之外:

r = ((double) rand() / (RAND_MAX)) + 1;

2
添加一个可能会把它放在MIN_INT,但仍然是UB。
Pubby 2012年

2
由于所有溢出,加一会导致不确定的行为。这是错误和误导性的,因为它似乎可以工作。请更正您的答案或将其删除。
Luchian Grigore 2012年

我同意,我已经进行了相应的编辑。但是,这似乎是观察到的行为。
2012年

1
@LuchianGrigore:只是为了澄清,仅用于签名溢出。
GManNickG 2012年

3

没有。它可以0 <= r < 1,但是你原来是0 <= r <= 1

请注意,如果溢出,这可能导致未定义的行为RAND_MAX + 1


1
RAND_MAX不会溢出未签名的类型,因此您可以将其
强制转换

3

我的猜测是RAND_MAX等于INT_MAX,因此您将其溢出为负数。

只是这样做:

r = ((double) rand() / (RAND_MAX)) + 1;

甚至更好,请使用C ++ 11的随机数生成器。


正如您小心地指出其他人的答案一样,“将其溢出为否定”只是在OP观察到的情况下发生的事情,而不是可靠的描述,因为溢出是UB,因此生成的程序可能根本不起作用。
underscore_d

1

这是正确的方法:

double randd() {
  return (double)rand() / ((double)RAND_MAX + 1);
}

要么

double randd() {
  return (double)rand() / (RAND_MAX + 1.0);
}

这样可以修复带符号的整数溢出UB,同时保留OP其余语义的完整性,但是通过阅读对它们真正尝试执行的操作的描述,我们可以清楚地看到它仍然无法产生预期的结果,因为它们是错误的将添加项放在该位置。
underscore_d

为什么+1?假设RAND_MAX = 2,所以rand()返回0或1或2,如果您希望它返回0到1之间的数字,是否不应该将它除以2而不是3?
Tomer Wolberg,

0(包括),1(排除)。如果包含1,则1的概率很小。
金·塔基翁

0

就我而言(我正在使用VS 2017),以下简单代码可以正常工作:

#include "pch.h"
#include <iostream>
#include <stdlib.h>
#include <time.h>

int main()
{
    srand(time(NULL));

    for (int i = 1000; i > 0; i--) //try it thousand times
    {
        int randnum = (double)rand() / ((double)RAND_MAX + 1);
        std::cout << " rnum: " << rand()%2 ;
    }
} 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.