【问题标题】:std::uniform_real_distribution always returns infinitystd::uniform_real_distribution 总是返回无穷大
【发布时间】:2021-03-29 12:22:06
【问题描述】:

当我运行这段代码时:

double getRandomDouble() {
    static std::mt19937 entropy_ = std::mt19937();
    std::uniform_real_distribution<double> distribution;
    distribution.param(typename decltype(distribution)::param_type(std::numeric_limits<double>::lowest(),
                                                                   std::numeric_limits<double>::max()));
    return distribution(entropy_);
}

它总是返回无穷大(至少在 GCC8.1 和 clang 11.0.1 中。在 MSVC 14.16.27023 中它断言)

Here is a working demonstration in GodBolt

我希望这个函数返回任何随机的双精度值,这里发生了什么?

【问题讨论】:

  • lowest()更改为min()
  • @TedLyngmo 但它不会做我想做的事,因为它只会返回正值......
  • 啊...好吧,-max()/2.0, max()/2.0 可能是一个不错的范围。 example
  • @TedLyngmo 虽然这可能会“解决”问题,但它仍然不能解释发生了什么......
  • 来自rand.dist.uni.real#1 p(x|a,b)=1/(b−a),但max()-lowest() 使结果有问题:-/

标签: c++ random numeric-limits


【解决方案1】:

参数的选择违反了std::uniform_real_distribution的前提条件(c.f.§26.6.9.2.2.2)。

前提条件是a ≤ bb - a ≤ numeric_limits&lt;T&gt;::max(),其中ab 是分布的最小值和最大值。使用numeric_limits&lt;double&gt;::lowest()numeric_limits&lt;double&gt;::max() 会违背这一点。

根据Ted 的建议,使用a = -max()/2.0b = max()/2.0 会是更好的选择。

【讨论】:

  • [0; max) * randomSign 可能更适合拥有“整个”双精度范围。
【解决方案2】:

uniform_real_distribution 的强制实现基本上是在[0,1) 中均匀分布,然后乘以(b-a) 再加上a 映射到[a,b)

这是一个很好的面向速度的实现,但是当应用于极端情况时它会失败。在您的情况下,计算 b-a 导致无穷大,随后整个计算失败。

【讨论】:

  • 所以这是编译器实现的错误?
  • @Troyseph 我相信这是 C++ 标准规定的。该提案有几个错误。一般来说,我不确定这个问题是否可以称为错误,因为只有在非常奇怪的情况下才需要在整个双精度范围内的随机数,并且需要大量额外的计算和验证以确保它正常工作。对于绝大多数用户来说,这只会不必要地减慢程序的速度。
【解决方案3】:

你有一个数字问题:分布使用概率函数:1 / (b - a),在这种情况下,b - a 超出了double 的范围(这意味着inf,因此所有数字都是inf)。您可以通过降低限制来修复它,如下例所示。

此外,您始终使用新的随机数生成器,并且由于种子相同,它始终生成相同的数字。您可以将生成器置于函数外部,以便它继续生成新数字,或者更好地关联一个随机设备。

#include <random>
#include <iostream>

double getRandomDouble() {
    std::random_device rd; // <<
    std::mt19937 entropy_ = std::mt19937(rd()); // <<
    std::uniform_real_distribution<double> distribution;

    using param = typename decltype(distribution)::param_type;
    distribution.param(param(std::numeric_limits<double>::lowest() / 2, 
                             std::numeric_limits<double>::max() / 2));
    return distribution(entropy_);
}

int main()
{
    for (int i = 0; i < 10; ++i) {
        std::cout << "Random number: " << getRandomDouble() << "\n";
    }
    return 0;
}

你可以测试一下here

【讨论】:

  • 更新了GodBolt,这不是问题,它仍然总是返回无穷大
  • Demo 的代码,使用 lowest 而不是 min 作为 OP。 -> inf
  • “范围 [-1, 1] 乘以max”不是会损失很多粒度吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-16
相关资源
最近更新 更多