你如何从 C++ 生成一个在 0 和 1 之间均匀分布的随机双精度值?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1340729/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 19:37:54  来源:igfitidea点击:

How do you generate a random double uniformly distributed between 0 and 1 from C++?

c++random

提问by static_rtti

How do you generate a random double uniformly distributed between 0 and 1 from C++?

你如何从 C++ 生成一个在 0 和 1 之间均匀分布的随机双精度值?

Of course I can think of some answers, but I'd like to know what the standard practice is, to have:

当然,我可以想到一些答案,但我想知道标准做法是什么,有:

  • Good standards compliance
  • Good randomness
  • Good speed
  • 良好的标准合规性
  • 随机性好
  • 速度不错

(speed is more important than randomness for my application).

(对于我的应用程序来说,速度比随机性更重要)。

Thanks a lot!

非常感谢!

PS: In case that matters, my target platforms are Linux and Windows.

PS:以防万一,我的目标平台是 Linux 和 Windows。

采纳答案by Shafik Yaghmour

In C++11 and C++14 we have much better options with the random header. The presentation rand() Considered Harmfulby Stephan T. Lavavejexplains why we should eschew the use of rand()in C++ in favor of the randomheader and N3924: Discouraging rand() in C++14further reinforces this point.

在 C++11 和 C++14 中,我们有更好的随机头选项。呈现兰特()是有害的斯蒂芬T. Lavavej解释了为什么我们应该避开使用rand()C ++中赞成的random头和N3924:在C ++ 14个劝阻兰特()进一步强化了这一点。

The example below is a modified version of the sample code on the cppreference site and uses the std::mersenne_twister_engineengine and the std::uniform_real_distributionwhich generates numbers in the [0,1)range (see it live):

下面的示例是 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';
    }
}

output will be similar to the following:

输出将类似于以下内容:

0 ************************
1 *************************

Since the post mentioned that speed was important then we should consider the cppreference section that describes the different random number engines (emphasis mine):

既然帖子提到速度很重要,那么我们应该考虑描述不同随机数引擎的 cppreference 部分(强调我的):

The choice of which engine to use involves a number of tradeoffs*: the **linear congruential engine is moderately fastand has a very small storage requirement for state. The lagged Fibonacci generators are very fast even on processors without advanced arithmetic instructionsets, at the expense of greater state storage and sometimes less desirable spectral characteristics. The Mersenne twister is slower and has greater state storage requirementsbut with the right parameters has the longest non-repeating sequence with the most desirable spectral characteristics (for a given definition of desirable).

选择使用哪个引擎涉及许多权衡*:**线性同余引擎速度适中,并且对状态的存储需求非常小。即使在没有高级算术指令集的处理器上滞后的斐波那契生成器也非常快,代价是更大的状态存储和有时不太理想的频谱特性。的梅森捻线机是较慢并且具有更大的状态的存储要求,但是具有正确的参数具有最期望的光谱特性的最长非重复序列(对于期望在给定的定义)。

So if there is a desire for a faster generator perhaps ranlux24_baseor ranlux48_baseare better choices over mt19937.

所以,如果有一个愿望更快的发电机或许ranlux24_baseranlux48_base超过更好的选择mt19937

rand()

兰特()

If you forced to use rand()then the C FAQfor a guide on How can I generate floating-point random numbers?, gives us an example similar to this for generating an on the interval [0,1):

如果强行使用rand(),则Ç常见问题上的指导我如何生成浮点随机数?,给了我们一个类似的例子,用于在区间上生成一个[0,1)

#include <stdlib.h>

double randZeroToOne()
{
    return rand() / (RAND_MAX + 1.);
}

and to generate a random number in the range from [M,N):

并在以下范围内生成一个随机数[M,N)

double randMToN(double M, double N)
{
    return M + (rand() / ( RAND_MAX / (N-M) ) ) ;  
}

回答by Elemental

An old school solution like:

一个老式的解决方案,如:

double X=((double)rand()/(double)RAND_MAX);

Should meet all your criteria (portable, standard and fast). obviously the random number generated has to be seeded the standard procedure is something like:

应满足您的所有标准(便携、标准和快速)。显然,生成的随机数必须被播种标准程序是这样的:

srand((unsigned)time(NULL));

回答by Vijay Mathew

The random_real classfrom the Boost random libraryis what you need.

random_real类升压随机库是你需要的。

回答by John D. Cook

Here's how you'd do it if you were using C++ TR1.

如果您使用的是C++ TR1 ,您将如何做到这一点。

回答by suszterpatt

If speed is your primary concern, then I'd simply go with

如果速度是您最关心的问题,那么我会选择

double r = (double)rand() / (double)RAND_MAX;

回答by DarthGizka

The C++11 standard library contains a decent framework and a couple of serviceable generators, which is perfectly sufficient for homework assignments and off-the-cuff use.

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

However, for production-grade code you should know exactly what the specific properties of the various generators are before you use them, since all of them have their caveats. Also, none of them passes standard tests for PRNGs like TestU01, except for the ranlux generators if used with a generous luxury factor.

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

If you want solid, repeatable results then you have to bring your own generator.

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

If you want portability then you have to bring your own generator.

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

If you can live with restricted portability then you can use boost, or the C++11 framework in conjunction with your own generator(s).

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

More detail - including code for a simple yet fast generator of excellent quality and copious links - can be found in my answers to similar topics:

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

For professional uniform floating-point deviates there are two more issues to consider:

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

  • open vs. half-open vs. closed range, i.e. (0,1), [0, 1) or [0,1]
  • method of conversion from integral to floating-point (precision, speed)
  • 开放与半开放与封闭范围,即 (0,1)、[0, 1) 或 [0,1]
  • 从整数到浮点的转换方法(精度、速度)

Both are actually two sides of the same coin, as the method of conversion takes care of the inclusion/exclusion of 0 and 1. Here are three different methods for the half-open interval:

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

(random_uint32()and random_uint64()are placeholders for your actual functions and would normally be passed as template parameters)

random_uint32()并且random_uint64()是实际函数的占位符,通常作为模板参数传递)

Method ademonstrates how to create a uniform deviate that is not biassed by excess precision for lower values; the code for 64-bit is not shown because it is simpler and just involves masking off 11 bits. The distribution is uniform for all functions but without this trick there would be more different values in the area closer to 0 than elsewhere (finer grid spacing due to the varying ulp).

方法a演示了如何创建一个均匀的偏差,该偏差不会因较低值的过度精度而产生偏差;64 位的代码没有显示,因为它更简单,只涉及屏蔽 11 位。所有函数的分布都是均匀的,但如果没有这个技巧,在更接近 0 的区域会比其他地方有更多不同的值(由于 ulp 的变化,网格间距更细)。

Method cshows how to get a uniform deviate faster on certain popular platforms where the FPU knows only a signed 64-bit integral type. What you see most often is method bbut there the compiler has to generate lots of extra code under the hood to preserve the unsigned semantics.

方法c展示了如何在 FPU 只知道有符号 64 位整数类型的某些流行平台上更快地获得均匀偏差。您最常看到的是方法b,但编译器必须在后台生成大量额外代码以保留无符号语义。

Mix and match these principles to create your own tailored solution.

混合和匹配这些原则以创建您自己的定制解决方案。

All this is explained in Jürgen Doornik's excellent paper Conversion of High-Period Random Numbers to Floating Point.

所有这一切都在 Jürgen Doornik 的优秀论文Conversion of High-Period Random Numbers to Floating Point 中得到了解释

回答by Amarjit Datta

First include stdlib.h

首先包含 stdlib.h

#include<stdlib.h>

Then following can be a function to generate random double number between a range in C programming language.

那么下面可以是C编程语言中在一个范围之间生成随机双数的函数。

double randomDouble() {
    double lowerRange = 1.0;
    double upperRange = 10.0;
    return ((double)rand() * (upperRange - lowerRange)) / (double)RAND_MAX + lowerRange;
}

Here RAND_MAX is defined in stdlib.h

这里的 RAND_MAX 在 stdlib.h 中定义

回答by Johnny Cage

As I see it, there are three ways to go with this,

在我看来,有三种方法可以解决这个问题,

1) The easy way.

1)简单的方法。

double rand_easy(void)
{       return (double) rand() / (RAND_MAX + 1.0);
}

2) The safe way (standard conforming).

2)安全方式(符合标准)。

double rand_safe(void)
{
        double limit = pow(2.0, DBL_MANT_DIG);
        double denom = RAND_MAX + 1.0;
        double denom_to_k = 1.0;
        double numer = 0.0;

        for ( ; denom_to_k < limit; denom_to_k *= denom )
           numer += rand() * denom_to_k;

        double result = numer / denom_to_k;
        if (result == 1.0)
           result -= DBL_EPSILON/2;
        assert(result != 1.0);
        return result;
}

3) The custom way.

3)自定义方式。

By eliminating rand()we no longer have to worry about the idiosyncrasies of any particular version, which gives us more leeway in our own implementation.

通过消除,rand()我们不再需要担心任何特定版本的特性,这为我们自己的实现提供了更多的余地。

Note:Period of the generator used here is ≅ 1.8e+19.

注:这里使用的发电机周期为≅ 1.8e+19。

#define RANDMAX (-1ULL)
uint64_t custom_lcg(uint_fast64_t* next)
{       return *next = *next * 2862933555777941757ULL + 3037000493ULL;
}

uint_fast64_t internal_next;
void seed_fast(uint64_t seed)
{       internal_next = seed;
}

double rand_fast(void)
{
#define SHR_BIT (64 - (DBL_MANT_DIG-1))
        union {
            double f; uint64_t i;
        } u;
        u.f = 1.0;
        u.i = u.i | (custom_lcg(&internal_next) >> SHR_BIT);
        return u.f - 1.0;
}

Whatever the choice, functionality may be extended as follows,

无论选择什么,功能都可以扩展如下,

double rand_dist(double min, double max)
{       return rand_fast() * (max - min) + min;
}

double rand_open(void)
{       return rand_dist(DBL_EPSILON, 1.0);
}

double rand_closed(void)
{       return rand_dist(0.0, 1.0 + DBL_EPSILON);
}

Final notes:The fast version - while written in C - may be adapted for use in C++ to be used as a replacement for std::generate_canonical, and will work for any generator emitting values with sufficient significant bits.

最后说明:快速版本 - 虽然用 C 编写 - 可能适用于 C++ 以用作 的替代品std::generate_canonical,并将适用于任何发出具有足够有效位的值的生成器。

Most 64 bit generators take advantage of their full width, so this can likely be used without modification (shift adjustment). e.g. this works as-is with the std::mt19937_64engine.

大多数 64 位生成器利用它们的全宽度,因此这可能无需修改(移位调整)即可使用。例如,这与std::mt19937_64引擎一样工作。

回答by Abhay

Well considering simplicity and speed as your primary criteria, you can add a small generic helper like this :-

考虑到简单性和速度作为您的主要标准,您可以添加一个像这样的小型通用助手:-

  // C++ rand generates random numbers between 0 and RAND_MAX. This is quite a big range
  // Normally one would want the generated random number within a range to be really
  // useful. So the arguments have default values which can be overridden by the caller
  int nextRandomNum(int low = 0, int high = 100) const {
    int range = (high - low) + 1;
    // this modulo operation does not generate a truly uniformly distributed random number
    // in the span (since in most cases lower numbers are slightly more likely), 
    // but it is generally a good approximation for short spans. Use it if essential
    //int res = ( std::rand() % high + low );
    int res = low + static_cast<int>( ( range * std::rand() / ( RAND_MAX + 1.0) ) );
    return res;
  }

Random number generation is a well studied, complex and advanced topic. You can find some simple but useful algorithms here apart from the ones mentioned in other answers:-

随机数生成是一个经过充分研究、复杂且高级的主题。除了其他答案中提到的算法之外,您还可以在这里找到一些简单但有用的算法:-

Eternally Confuzzled

永远迷茫

回答by Ryan

You could try the Mersenne Twister algorithm.

您可以尝试使用 Mersenne Twister 算法。

http://en.wikipedia.org/wiki/Mersenne_twister

http://en.wikipedia.org/wiki/Mersenne_twister

It has a good blend of speed and randomness, and a GPL implementation.

它很好地融合了速度和随机性,以及 GPL 实现。