如何从C ++生成0到1之间均匀分布的随机双精度?
当然我可以想到一些答案,但我想知道标准做法是什么,有:
(速度比我的应用程序的随机性更重要)。
非常感谢!
PS:如果重要,我的目标平台是Linux和Windows。
答案 0 :(得分:55)
旧学校解决方案如:
double X=((double)rand()/(double)RAND_MAX);
应符合您的所有标准(便携式,标准型和快速型)。 很明显,生成的随机数必须是标准程序,如下所示:
srand((unsigned)time(NULL));
答案 1 :(得分:31)
在C ++ 11和C ++ 14中,random header有更好的选择。 Stephan T. Lavavej 的演示文稿rand() Considered Harmful解释了为什么我们应该避免在C ++中使用rand()
而支持random
标题和N3924: Discouraging rand() in C++14进一步强调了这一点。
以下示例是cppreference网站上示例代码的修改版本,并使用std::mersenne_twister_engine引擎和std::uniform_real_distribution生成[0,1)
范围内的数字( { {3}} 的):
#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';
}
}
输出类似于以下内容:
0 ************************
1 *************************
由于帖子提到速度很重要,所以我们应该考虑描述不同随机数引擎的cppreference部分(强调我的):
选择使用哪种引擎涉及许多权衡*: **线性同余引擎适度快并且非常小 国家的储存要求。 滞后的斐波纳契生成器是 甚至在没有高级算术指令的处理器上也非常快 设置,以更大的状态存储为代价,有时更少 理想的光谱特性。 Mersenne twister慢了 具有更大的状态存储要求但具有正确的参数 具有最长的非重复序列,具有最理想的 光谱特征(对于给定的理想定义)。
因此,如果需要更快的生成器,see it live或ranlux24_base可能比ranlux48_base更好的选择。
兰特()
如果您被迫rand()
使用 C常见问题解答作为mt19937上的指南,请为我们提供一个与此类似的示例,用于生成间隔{{1 }}:
[0,1)
并生成#include <stdlib.h>
double randZeroToOne()
{
return rand() / (RAND_MAX + 1.);
}
:
[M,N)
答案 2 :(得分:6)
random_real class中的Boost random library就是您所需要的。
答案 3 :(得分:5)
如果您使用C++ TR1,请按以下步骤操作。
答案 4 :(得分:3)
如果速度是您最关心的问题,那么我只需要使用
double r = (double)rand() / (double)RAND_MAX;
答案 5 :(得分:3)
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;
}
(random_uint32()
和random_uint64()
是您实际功能的占位符,通常会作为模板参数传递)
方法 a 演示如何创建一个均匀偏差,不会因较低值的过度精度而产生偏差; 64位代码没有显示,因为它更简单,只需要屏蔽掉11位。所有函数的分布均匀,但没有这个技巧,在接近0的区域中将有比其他地方更多的不同值(由于变化的ulp,网格间距更细)。
方法 c 显示了如何在某些流行平台上更快地获得统一偏差,其中FPU只知道带符号的64位整数类型。您最常见的是方法 b 但是编译器必须在引擎盖下生成大量额外代码以保留无符号语义。
混合并匹配这些原则,以创建您自己的定制解决方案。
JürgenDoornik的优秀论文Conversion of High-Period Random Numbers to Floating Point解释了所有这些。
答案 6 :(得分:2)
首先包括stdlib.h
#include<stdlib.h>
然后,下面的函数可以在C编程语言中生成一个范围之间的随机双数。
double randomDouble() {
double lowerRange = 1.0;
double upperRange = 10.0;
return ((double)rand() * (upperRange - lowerRange)) / (double)RAND_MAX + lowerRange;
}
这里RAND_MAX在stdlib.h中定义
答案 7 :(得分:1)
正如我所看到的,有三种方法可以解决这个问题,
1)简单的方法。
double rand_easy(void)
{ return (double) rand() / (RAND_MAX + 1.0);
}
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)自定义方式。
通过消除rand()
,我们不再需要担心任何特定版本的特性,这为我们自己的实现提供了更多的余地。
注意:此处使用的发电机周期为≅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;
}
无论选择何种,功能都可以扩展如下,
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);
}
最后的注释:快速版本 - 用C语言编写 - 可以在C ++中使用,以用作std::generate_canonical
的替代,并且可以用于任何发射值的发生器有足够的重要位。
大多数64位发生器利用它们的全宽,因此可以不加修改地使用(移位调整)。例如这与std::mt19937_64
引擎一样正常。
答案 8 :(得分:0)
考虑到简单性和速度是您的主要标准,您可以添加如下的小型通用助手: -
// 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;
}
随机数生成是一个经过充分研究,复杂和高级的主题。除了其他答案中提到的算法之外,你可以找到一些简单但有用的算法: -
答案 9 :(得分:-1)
答案 10 :(得分:-1)
这是我最终用于满足我的需求:
int range_upper_bound = 12345;
int random_number =((double)rand()/(double)range_upper_bound);
答案 11 :(得分:-2)
double randDouble()
{
double out;
out = (double)rand()/(RAND_MAX + 1); //each iteration produces a number in [0, 1)
out = (rand() + out)/RAND_MAX;
out = (rand() + out)/RAND_MAX;
out = (rand() + out)/RAND_MAX;
out = (rand() + out)/RAND_MAX;
out = (rand() + out)/RAND_MAX;
return out;
}
不如double X=((double)rand()/(double)RAND_MAX);
快,但分发更好。该算法仅给出RAND_MAX均匀间隔的返回值选择;这个给出了RANDMAX ^ 6,所以它的分布只受到double的精度的限制。
如果你想要一个long double,只需添加几个迭代。如果你想要[0,1]而不是[0,1]中的数字,只需要将第4行读为out = (double)rand()/(RAND_MAX);
。
答案 12 :(得分:-2)
//Returns a random number in the range (0.0f, 1.0f).
// 0111 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
// seee eeee eeee vvvv vvvv vvvv vvvv vvvv vvvv vvvv vvvv vvvv vvvv vvvv vvvv vvvv
// sign = 's'
// exponent = 'e'
// value = 'v'
double DoubleRand() {
typedef unsigned long long uint64;
uint64 ret = 0;
for (int i = 0; i < 13; i++) {
ret |= ((uint64) (rand() % 16) << i * 4);
}
if (ret == 0) {
return rand() % 2 ? 1.0f : 0.0f;
}
uint64 retb = ret;
unsigned int exp = 0x3ff;
retb = ret | ((uint64) exp << 52);
double *tmp = (double*) &retb;
double retval = *tmp;
while (retval > 1.0f || retval < 0.0f) {
retval = *(tmp = (double*) &(retb = ret | ((uint64) (exp--) << 52)));
}
if (rand() % 2) {
retval -= 0.5f;
}
return retval;
}
这应该可以解决问题,我使用this维基百科的文章来帮助创建这个。我认为它与drand48();