C++ 快速正态随机数生成器
C++ fast normal random number generator
我正在使用 mt19937
生成器生成正常的随机数,如下所示:
normal_distribution<double> normalDistr(0, 1);
mt19937 generator(123);
vector<double> randNums(1000000);
for (size_t i = 0; i != 1000000; ++i)
{
randNums[i] = normalDistr(generator);
}
上面的代码有效,但是由于我在我的代码中生成了超过 1 亿个正常随机数,所以上面的代码非常慢。
有没有更快的方法生成正态随机数?
以下是有关如何使用代码的一些背景信息:
- 随机数的质量并不那么重要
- 数字的精度不是那么重要,
double
或float
都可以
- 正态分布始终具有均值 = 0 和西格玛 = 1
编辑:
@Dúthomhas,安德鲁:
分析后,以下函数占用了超过 50% 的时间:
std::normal_distribution<double>::_Eval<std::mersenne_twister_engine<unsigned int,32,624,397,31,2567483615,11,4294967295,7,2636928640,15,4022730752,18,1812433253> >
如果确实是生成器导致性能下降那么使用普通的rand
函数(需要成对绘制数字),将0、1转换为float或double然后应用 Box Muller 变换。
这在时间上很难被击败,但请注意,统计属性并不比 rand
。
gasdev 的数字食谱例程可以执行此操作 - 您应该可以下载一份副本。
您还需要查看 std::vector 储备而不是调整大小。它将让您一次性获得所需的所有内存。我假设您不需要一次所有 1 亿个双打?
最重要的是,你真的需要同时100,000,000个随机数吗?将所有这些数据写入 RAM 以及随后从 RAM 中读取这些数据不可避免地需要大量时间。如果您一次只需要一个随机数,则应避免这种情况。
假设您确实需要 RAM 中的所有这些数字,那么您应该首先
如果您真的想知道 CPU 时间 spent/lost.
在哪里,请分析您的代码
其次,你应该避免不必要的数据重新分配和初始化。结合使用 std::vector::reserve(final_size)
和 std::vector::push_back()
.
最容易做到这一点
第三,您可以使用比 std::mt19937
更快的 RNG。当数字的质量很重要时,建议使用 RNG。 online documentation says that the lagged Fibonacci generator (implemented in std:: subtract_with_carry_engine
) is fast, but it may not have a long enough recurrence period -- you must check this. Alternatively, you may want to use std::min_stdrand
(which uses the linear congruential generator)
std::vector<double> make_normal_random(std::size_t number,
std::uint_fast32_t seed)
{
std::normal_distribution<double> normalDistr(0,1);
std::min_stdrand generator(seed);
std::vector<double> randNums;
randNums.reserve(number);
while(number--)
randNums.push_back(normalDistr(generator));
return randNums;
}
我正在使用 mt19937
生成器生成正常的随机数,如下所示:
normal_distribution<double> normalDistr(0, 1);
mt19937 generator(123);
vector<double> randNums(1000000);
for (size_t i = 0; i != 1000000; ++i)
{
randNums[i] = normalDistr(generator);
}
上面的代码有效,但是由于我在我的代码中生成了超过 1 亿个正常随机数,所以上面的代码非常慢。
有没有更快的方法生成正态随机数?
以下是有关如何使用代码的一些背景信息:
- 随机数的质量并不那么重要
- 数字的精度不是那么重要,
double
或float
都可以 - 正态分布始终具有均值 = 0 和西格玛 = 1
编辑:
@Dúthomhas,安德鲁:
分析后,以下函数占用了超过 50% 的时间:
std::normal_distribution<double>::_Eval<std::mersenne_twister_engine<unsigned int,32,624,397,31,2567483615,11,4294967295,7,2636928640,15,4022730752,18,1812433253> >
如果确实是生成器导致性能下降那么使用普通的rand
函数(需要成对绘制数字),将0、1转换为float或double然后应用 Box Muller 变换。
这在时间上很难被击败,但请注意,统计属性并不比 rand
。
gasdev 的数字食谱例程可以执行此操作 - 您应该可以下载一份副本。
您还需要查看 std::vector 储备而不是调整大小。它将让您一次性获得所需的所有内存。我假设您不需要一次所有 1 亿个双打?
最重要的是,你真的需要同时100,000,000个随机数吗?将所有这些数据写入 RAM 以及随后从 RAM 中读取这些数据不可避免地需要大量时间。如果您一次只需要一个随机数,则应避免这种情况。
假设您确实需要 RAM 中的所有这些数字,那么您应该首先 如果您真的想知道 CPU 时间 spent/lost.
在哪里,请分析您的代码其次,你应该避免不必要的数据重新分配和初始化。结合使用 std::vector::reserve(final_size)
和 std::vector::push_back()
.
第三,您可以使用比 std::mt19937
更快的 RNG。当数字的质量很重要时,建议使用 RNG。 online documentation says that the lagged Fibonacci generator (implemented in std:: subtract_with_carry_engine
) is fast, but it may not have a long enough recurrence period -- you must check this. Alternatively, you may want to use std::min_stdrand
(which uses the linear congruential generator)
std::vector<double> make_normal_random(std::size_t number,
std::uint_fast32_t seed)
{
std::normal_distribution<double> normalDistr(0,1);
std::min_stdrand generator(seed);
std::vector<double> randNums;
randNums.reserve(number);
while(number--)
randNums.push_back(normalDistr(generator));
return randNums;
}