【问题标题】:function for random numbers (C++11)随机数函数 (C++11)
【发布时间】:2014-01-16 15:30:49
【问题描述】:

我正在重写这个函数:

long rng(long low, long high)
{
    return low + long((high - low + 1) * double(rand() / double(RAND_MAX + 1.0)));
}

将其更新为 C++11 和 Mersenne Twister。

long rng(long low, long high)
{
    static std::mt19937 rnumber; // Mersenne Twister
    std::uniform_int_distribution<unsigned> u (low, high);
    return u(rnumber);
}

现在这似乎可行,但我的 C++11 书指出,在类似情况下,引擎和分发器都应该是“静态的”。问题是我想调用具有不同范围的函数,如果我将分配器设为静态,那么范围总是相同的。

那么应该如何正确处理呢?

另外两个问题:

  • 为什么 Stroustrup 的书似乎建议我应该使用 {low, high} 而不是 (low, high)?有什么区别?

  • 在 gcc 4.8.1 和没有优化的情况下,rand 的速度可能是 Mersenne 的两倍,而使用 -O3 rand 具有相同的性能,而 Mersenne 实际上甚至比 rand 更快?

【问题讨论】:

  • {} 是“统一初始化”,Stroustrup 建议这样做,因为在任何地方使用它都会减少混乱。
  • @OliCharlesworth 我猜他知道引擎必须是静态的。他想知道为什么分布也应该如此。因为在他的情况下它不能。
  • 我认为你真正的问题是没有种子。
  • 另外,请使用&lt;long&gt; 进行分发。它应该命名要返回的类型;在你的情况下long,而不是unsigned

标签: c++ c++11 random


【解决方案1】:

您不必事先指定分配的限制。

#include <iostream>
#include <ostream>
#include <random>

unsigned long rng(unsigned long low, unsigned long high)
{
    static std::mt19937 engine((std::random_device()()));
    static std::uniform_int_distribution<unsigned long> dist;

    std::uniform_int_distribution<unsigned long>::param_type p(low, high);

    return dist(engine, p);
}

int main()
{
    const unsigned int n = 10;
    const int m = 24;
    for (unsigned int k = 2; k != n + 1; ++k)
    {
        for (int j = 0; j != m; ++j)
        {
            // Print out 24 numbers from the range [0, k)
            std::cout << rng(0, k - 1) << " ";
        }
        std::cout << std::endl;
    }

    return 0;
}

【讨论】:

    【解决方案2】:

    使用operator()时允许分配计算多个随机数,只要g.operator()的调用次数为摊销常数即可。

    因此使用a static distribution and a non-static one有很大区别:

    #include <iostream>
    #include <random>
    
    template <class Generator>
    double use_rand(Generator & g){
        std::normal_distribution<double> d;
        return d(g);
    }
    
    template <class Generator>
    double use_rand_static(Generator & g){
        static std::normal_distribution<double> d;
        return d(g);
    }
    
    int main(){
        std::mt19937 rnumber{5};
        std::cout << "non-static version:\n";
        std::cout << use_rand(rnumber) << "\n";
        std::cout << use_rand(rnumber) << "\n\n";
    
        rnumber.seed(5);
        std::cout << "static version:\n";
        std::cout << use_rand_static(rnumber) << "\n";
        std::cout << use_rand_static(rnumber) << std::endl;
        return 0;
    }
    
    非静态版本:
    0.107794
    -0.199707
    
    静态版:
    0.107794
    -0.0306363

    如您所见,第二个值不同。其原因隐藏在normal_distribution::operator() 的实现中,它每隔一次迭代计算两个值。

    这个实现定义的行为实际上是你应该使用相同分布的原因,只要你想使用来自相同概率空间的值(又名static)。但是,由于您要更改 lowhigh 经常要更改概率空间,因此可以使用新分布(创建大多数分布实际上非常便宜,但是为每个值使用新分布可以获得贵)。

    【讨论】:

    • 我试图在一个标题中放入一些可选的分销商,使其成为静态的。性能方面,实际上并没有什么区别,实际上函数中的非静态分配器似乎更快。相反,我尝试使用 1, 100 范围,它使函数 TWICE 比 1, 5 范围快。不知道为什么。
    • @Abalieno:缩小比例。如果要使用 [1..5],则只能使用 [-2^63 ... 2^63 - 1] 中所有值的 ~83%。如果您使用的是 [1..100],您可以使用 URNG 范围内所有值的 ~99%,这会产生巨大的差异。请注意,如果您为这些值使用更好的类型,则这种差异会消失,例如unsigned char.
    • @Zeta:你为什么只使用~83%?我认为您可以使用除 0xFFFFFFFFFFFFFFFF%5 -&gt; 1 之外的所有值...
    【解决方案3】:

    分发对象是需要一些初始化的对象。如果您可以重复使用它们,那么重复使用它们将比每次使用时都创建它们更有效。由于随机数的典型用途是为相同的范围生成许多值,因此可以删除额外的结构。当用不同的随机数生成器替换 rand() 的使用时,传递对象可能是不可行的。对于新创建的代码,保留足够的上下文以具有分发对象也是合理的。

    关于您的附加问题:

    1. 使用大括号代替圆括号使用统一初始化语法(当然,这远非普遍适用)。优点之一是它拒绝进行缩小转换。
    2. C++11 随机数生成器中的计算可能是内联的。可能有足够的优化空间让 gcc 使用-O3 更快地进行计算。当然,编译器也有可能决定您不使用随机数的结果并省略实际使用它的结果。

    【讨论】:

      猜你喜欢
      • 2012-12-10
      • 1970-01-01
      • 1970-01-01
      • 2013-11-09
      • 2013-06-27
      • 2011-06-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多