【问题标题】:rand() between 0 and 1rand() 介于 0 和 1 之间
【发布时间】:2012-04-10 08:38:49
【问题描述】:

所以下面的代码使得 0

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

为什么拥有r = ((double) rand() / (RAND_MAX + 1)) 会使 -1

不应该在 RAND_MAX 上加一使得 1

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

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

【问题讨论】:

  • 尝试在最后一个)之后添加1
  • 请注意,无论如何你都应该使用uniform_real_distribution。如果您没有 C++11,请使用 boost version
  • 代数错误。如果r 是从零到m 范围内的随机数,则比率r/m 将在(0,1) 范围内,但r/(m+1) 将在(0, m/(m+1)) 范围内而不是@987654334 范围内@。如果m 非常大(与1 相比),那么m/(m+1) 大约为1,因此您的表达式r = ((double) rand() / (RAND_MAX + 1)) 将给出一个大约在(0,1) 范围内的随机数——也就是说,如果没有溢出。

标签: c++ random


【解决方案1】:

这完全是特定于实现的,但似乎在您正在使用的 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

【讨论】:

  • 你为什么要在RAND_MAX周围加上括号?
  • @ssc 强调与 OP 代码的区别。这些括号没有技术原因。
  • OP 尝试它的理由是错误的,但如果这是必要的,则可以通过添加 1.0 而不是 1 来避免 UB,这会将 RAND_MAX 强制转换为 double 类型因此避免整数溢出。
  • @ssc 因为是宏。您应该始终使用宏来执行此操作,除非您确切知道它是如何定义的。考虑这样一个定义 #define RAND_MAX 1+100 上面表达式的结果是什么? ;-)
  • @MadPhysicist 除非您确切知道,否则宏不是表达式而是单个数字。
【解决方案2】:

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

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

由于 rand() 的上述限制,生成 0(含)和 1(独占)之间的随机数的更好选择是以下 sn-p(类似于示例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) 替换为unif(1, 2),这很容易修改以生成介于1(包括)和2(不包括)之间的随机数。

【讨论】:

  • 天哪。我一直在尝试找出一个在 while 循环中使用的零对一随机数生成器,这实际上可能有效。我尝试了random_device 各种可能的方法,但得到了相同的输出。我不知道 0xfffffffff 是什么(十六进制代码?)以及右移到 32 ......两者都超出了我的想象。 (我是 C++ 新手,但它比 Rust 容易得多,恕我直言。)谢谢!
【解决方案3】:

不,因为 RAND_MAX 通常扩展为 MAX_INT。因此,添加一个(显然)将其置于 MIN_INT (尽管我被告知它应该是未定义的行为),因此符号的反转。

要得到你想要的,你需要将 +1 移到计算之外:

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

【讨论】:

  • 加一个可以把它放在MIN_INT,但它仍然是UB。
  • 加一会导致未定义的行为,因为所有溢出。这是错误且具有误导性的,因为它似乎有效。请更正您的答案或将其删除。
  • 我同意,我已经进行了相应的编辑。然而,这似乎是观察到的行为。
  • @LuchianGrigore:澄清一下,仅用于签名溢出。
【解决方案4】:

它没有。它使0 &lt;= r &lt; 1,但您的原件是0 &lt;= r &lt;= 1

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

【讨论】:

  • RAND_MAX 不能溢出无符号类型,因此您可以将其强制转换为无符号
【解决方案5】:

我的猜测是 RAND_MAX 等于 INT_MAX,所以你将它溢出为负数。

这样做:

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

或者更好的是,使用 C++11 的随机数生成器。

【讨论】:

  • 正如您对point out on someone else's answer 很小心,“将其溢出为负数”正是 OP 观察到的情况中发生的情况,并且不是可靠的描述,因为溢出是 UB,因此生成的程序可能做任何事。
【解决方案6】:

这是正确的方法:

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

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

【讨论】:

  • 这修复了有符号整数溢出 UB,同时保留了 OP 的其余语义完整,但是通过阅读他们真正想要做的事情的描述,我们可以清楚地看到它仍然没有产生期望的结果,因为他们将添加内容放在该位置是错误的。
  • 为什么要 +1?假设 RAND_MAX=2,所以 rand() 返回 0 或 1 或 2 如果你希望它返回一个介于 0 和 1 之间的数字,你不应该将它除以 2 而不是 3?
  • 0 是包含的,1 是排除的。如果 1 包含在内,则 1 的概率非常小。
  • 这适用于我的 g++。 (gcc 版本 10.2.1 20210110 (Debian 10.2.1-6)。对于测试程序来说简单且不太复杂。
【解决方案7】:

this-&gt;value = rand() % (this-&gt;max + 1);

似乎在 0 到 1++ 之间可以正常工作。

【讨论】:

    猜你喜欢
    • 2015-02-20
    • 1970-01-01
    • 1970-01-01
    • 2022-11-29
    • 1970-01-01
    • 2021-11-08
    • 1970-01-01
    • 2015-06-18
    • 1970-01-01
    相关资源
    最近更新 更多