如何在C++中生成0到1之间均匀分布的随机双精度?
当然我可以想到一些答案,但我想知道标准做法是什么,有:
(速度比我的应用程序的随机性更重要).
非常感谢!
PS:如果重要,我的目标平台是Linux和Windows.
Ele*_*tal 55
一个老派的解决方案如:
double X=((double)rand()/(double)RAND_MAX);
Run Code Online (Sandbox Code Playgroud)
应符合您的所有标准(便携,标准和快速).很明显,生成的随机数必须是标准程序,例如:
srand((unsigned)time(NULL));
Run Code Online (Sandbox Code Playgroud)
Sha*_*our 31
在C++ 11和C++ 14中,随机标头有更好的选择.演示rand()认为有害 的Stephan T. Lavavej 解释了为什么我们应该避免使用rand()C++来支持random标题和N3924:在C++ 14中阻止rand()进一步强化了这一点.
下面的示例是cppreference站点上示例代码的修改版本,并使用std :: mersenne_twister_engine引擎和std :: uniform_real_distribution生成[0,1)范围内的数字(请参见实时):
#include <iostream>
#include <iomanip>
#include <map>
#include <random>
int main()
{
std::random_device rd;
std::mt19937 e2(rd());
std::uniform_real_distribution<> dist(0, 1);
std::map<int, int> hist;
for (int n = 0; n < 10000; ++n) {
++hist[std::round(dist(e2))];
}
for (auto p : hist) {
std::cout << std::fixed << std::setprecision(1) << std::setw(2)
<< p.first << ' ' << std::string(p.second/200, '*') << '\n';
}
}
Run Code Online (Sandbox Code Playgroud)
输出将类似于以下内容:
0 ************************
1 *************************
Run Code Online (Sandbox Code Playgroud)
由于帖子提到速度很重要,所以我们应该考虑描述不同随机数引擎的cppreference部分(强调我的):
选择使用哪种引擎涉及许多权衡*:**线性同余引擎适度快,并且对状态的存储要求非常小.即使在没有高级算术指令 集的处理器上,滞后的Fibonacci发生器也非常快,代价是更大的状态存储和有时不太理想的频谱特性.的梅森捻线机是较慢并且具有更大的状态的存储要求,但是具有正确的参数具有最期望的光谱特性的最长非重复序列(对于期望在给定的定义).
因此,如果需要更快的发生器,也许ranlux24_base或ranlux48_base是mt19937的更好选择.
RAND()
如果强行使用rand(),则Ç常见问题上的指导我如何生成浮点随机数?,给我们一个类似的例子来生成一个间隔[0,1):
#include <stdlib.h>
double randZeroToOne()
{
return rand() / (RAND_MAX + 1.);
}
Run Code Online (Sandbox Code Playgroud)
并生成以下范围内的随机数[M,N):
double randMToN(double M, double N)
{
return M + (rand() / ( RAND_MAX / (N-M) ) ) ;
}
Run Code Online (Sandbox Code Playgroud)
来自Boost随机库的random_real类是您所需要的.
C++11 标准库包含一个不错的框架和几个可维护的生成器,这对于家庭作业和即兴使用来说是完全足够的。
但是,对于生产级代码,您应该在使用之前确切地知道各种生成器的具体属性是什么,因为它们都有各自的警告。此外,它们都没有通过像 TestU01 这样的 PRNG 的标准测试,除非与慷慨的豪华因素一起使用的 ranlux 生成器。
如果您想要可靠、可重复的结果,那么您必须携带自己的发电机。
如果您想要便携性,那么您必须带上自己的发电机。
如果您可以忍受受限的可移植性,那么您可以将 boost 或 C++11 框架与您自己的生成器结合使用。
更多细节 - 包括一个简单而快速的优秀质量和丰富链接的生成器的代码 - 可以在我对类似主题的回答中找到:
对于专业一致的浮点偏差,还有两个问题需要考虑:
两者实际上是同一枚硬币的两个面,因为转换方法负责包含/排除 0 和 1。以下是半开区间的三种不同方法:
// exact values computed with bc
#define POW2_M32 2.3283064365386962890625e-010
#define POW2_M64 5.421010862427522170037264004349e-020
double random_double_a ()
{
double lo = random_uint32() * POW2_M64;
return lo + random_uint32() * POW2_M32;
}
double random_double_b ()
{
return random_uint64() * POW2_M64;
}
double random_double_c ()
{
return int64_t(random_uint64()) * POW2_M64 + 0.5;
}
Run Code Online (Sandbox Code Playgroud)
(random_uint32()并且random_uint64()是实际函数的占位符,通常作为模板参数传递)
方法a演示了如何创建一个均匀的偏差,该偏差不会因较低值的过度精度而产生偏差;64 位的代码没有显示,因为它更简单,只涉及屏蔽 11 位。所有函数的分布都是均匀的,但如果没有这个技巧,在更接近 0 的区域会比其他地方有更多不同的值(由于 ulp 的变化,网格间距更细)。
方法c展示了如何在 FPU 只知道有符号 64 位整数类型的某些流行平台上更快地获得均匀偏差。您最常看到的是方法b,但编译器必须在后台生成大量额外代码以保留无符号语义。
混合和匹配这些原则以创建您自己的定制解决方案。
所有这一切都在 Jürgen Doornik 的优秀论文Conversion of High-Period Random Numbers to Floating Point 中得到了解释。
| 归档时间: |
|
| 查看次数: |
99009 次 |
| 最近记录: |