更多的呼唤mersenne twister比他们应该的

问题描述 投票:2回答:2

我当前的代码有一个特殊的问题。我正在编写一个程序,需要从两个分布(正态分布和一个真实分布)生成随机实数。生成这些值的代码存在于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 <>,uniform_real_dist的类型为std :: uniform_real_distribution <>

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

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

c++ c++11 c++14
2个回答
4
投票

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


6
投票

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.