如何在C++中生成0到1之间均匀分布的随机双精度?

sta*_*tti 60 c++ random

如何在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)

  • @Dave:只是为了挑剔:在这种情况下,输出将始终是[0,1]范围内的两倍,因此RAND_MAX的值唯一可以改变的是表达式可以返回的不同值的数量. (7认同)
  • 如果您在不同系统上寻找相同的范围和行为,请注意RAND_MAX.有些人的RAND_MAX为32k,其他人为2048k.总是最好检查. (6认同)
  • 它当然不是一个真正的均匀分布,但即使是_defining_恰恰对于浮点数也是一个挑战.(至少你应该期待的是,如果0 <x <1,则p(x)> 0 (4认同)
  • @suszterpatt:是的,这就是我的意思.鉴于他要求双精度,认为它可能是相关的!我应该更好地措辞. (2认同)
  • 这个答案肯定是过时的,使用srand/rand实际上并不统一.请参阅Shafik Yaghmour的回答中的链接. (2认同)

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_baseranlux48_basemt19937的更好选择.

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)


Vij*_*hew 6

来自Boost随机库random_real类是您所需要的.

  • 该库的文档非常稀缺.你有代码样本吗? (3认同)
  • 请看一下这个样本:http://www.boost.org/doc/libs/1_39_0/libs/random/random_demo.cpp (2认同)

Dar*_*zka 6

C++11 标准库包含一个不错的框架和几个可维护的生成器,这对于家庭作业和即兴使用来说是完全足够的。

但是,对于生产级代码,您应该在使用之前确切地知道各种生成器的具体属性是什么,因为它们都有各自的警告。此外,它们都没有通过像 TestU01 这样的 PRNG 的标准测试,除非与慷慨的豪华因素一起使用的 ranlux 生成器。

如果您想要可靠、可重复的结果,那么您必须携带自己的发电机。

如果您想要便携性,那么您必须带上自己的发电机。

如果您可以忍受受限的可移植性,那么您可以将 boost 或 C++11 框架与您自己的生成器结合使用。

更多细节 - 包括一个简单而快速的优秀质量和丰富链接的生成器的代码 - 可以在我对类似主题的回答中找到:

对于专业一致的浮点偏差,还有两个问题需要考虑:

  • 开放与半开放与封闭范围,即 (0,1)、[0, 1) 或 [0,1]
  • 从整数到浮点的转换方法(精度、速度)

两者实际上是同一枚硬币的两个面,因为转换方法负责包含/排除 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 中得到了解释


Joh*_*ook 5

如果你使用的是C++ TR1,那么你就是这样做的.

  • 在我看来这是实际声称给你一个UNIFORM实际分布的唯一实现 - 我上面的解决方案给出了MAX_RAND可能的值均匀分布在0到1的范围内,这是不完全相同的. (3认同)
  • 减去一个,仅限链接的答案是不受欢迎的 (2认同)
  • 要改进这个答案,请在此处简明扼要地解释答案并链接到博客以获取更多详细信息(如果适用) (2认同)