打电话给mersenne_twister比想象的要多

时间:2017-12-16 18:47:19

标签: c++ c++11 c++14

我的当前代码存在一个特殊问题。我正在编写一个程序,需要从两个分布(正态分布和一个真实分布)生成随机实数。生成这些值的代码存在于for循环中:

    char* buffer = new char[config.number_of_value * config.sizeof_line()];

    //...

    //Loop over how much values we want
    for(std::size_t i = 0; i < config.number_of_value; ++i)
    {
        //Calculates the offset where the current line begins (0, sizeof_line * 1, sizeof_line * 2, etc.)
        std::size_t line_offset = config.sizeof_line() * i;

        //The actual numbers we want to output to the file
        double x = next_uniform_real();
        double y = config.y_intercept + config.slope * x + next_normal_real();

        //Res is the number of character written. The character at buffer[res] is '\0', so we need
        //To get rid of it
        int res = sprintf((buffer + line_offset), "%f", x);
        buffer[line_offset + res] = '0';

        //Since we written double_rep_size character, we put the delimiter at double_rep_size index
        res = sprintf((buffer + line_offset + config.data_point_character_size() + sizeof(char)), "%f", y);
        buffer[line_offset + config.data_point_character_size() + sizeof(char) + res] = '0';
    }

    return buffer;

运行程序时,“number_of_value”的通常值为100'000。所以应该有10'000次调用next_uniform_real()和100'000调用next_normal_real()。奇怪的是,当我在Visual Studio 2017上用VSPerf配置这个代码时,我得到了对mersenne_twister生成器的227'242个调用,这是对每个函数的113'621调用。正如你所看到的那样,有超过3'621个电话。

任何人都可以帮我解决这个问题吗?

供参考,功能如下:

double generator::next_uniform_real()
{
    return uniform_real_dist(eng);
}

double generator::next_normal_real()
{
    return normal_dist(eng);
}

其中eng是std :: mt19937,当random_device没有熵时,使用random_device或time(0)播种。 normal_dist的类型为std :: normal_real_distribution&lt;&gt; 和uniform_real_dist的类型为std :: uniform_real_distribution&lt;&gt;

对于那些想知道的人,我正在填充一个char *缓冲区,这样我就可以对一个ostream进行一次写操作,而不是每次迭代循环一次。

(顺便说一句,如果有人知道更快的方式将float / double值写入char *或者生成实数的方法比这种方法更快,那真的很有帮助!)

2 个答案:

答案 0 :(得分:6)

std::normal_distribution的所有主要标准库实现都使用Marsaglia polar method。如维基百科文章中所述,

  

此程序需要对基础随机数生成器进行大约27%的评估(仅生成点的π/4≈79%位于单位圆内)。

您的号码听起来是正确的(每个号码1个RNG呼叫100000个统一实数加上每个号码1.27个RNG呼叫的100000个正常实数为227000)。

答案 1 :(得分:4)

想象一下,如果您尝试生成1到10之间的随机整数,并且您的输入源提供1到12之间的随机数(包括1和12)。如果你得到1到10之间的数字,你可以输出它。但是如果得到11,你必须得到1到12之间的另一个数字。因此,当将随机源与具有不同分布的随机输出匹配时,可能需要额外的调用。