如何从C ++中生成一个0到1之间的随机双分布?
如何从C ++中生成一个0到1之间的随机双分布?
当然,我可以想出一些答案,但我想知道标准做法是什么,有:
- 符合标准
- 随机性好
- 良好的速度
(速度比我的应用程序的随机性更重要)。
非常感谢!
PS:万一有问题,我的目标平台是Linux和Windows。
在C ++ 11和C ++ 14中,我们有更好的随机头选项。 演示rand()被Stephan T. Lavavej 认为是有害的 ,解释了为什么我们应该避免在C ++中使用rand()
来支持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'; } }
输出将类似于以下内容:
0 ************************ 1 *************************
既然post提到速度很重要,那么我们应该考虑描述不同随机数引擎的相关部分( 强调我的 ):
select使用哪种引擎需要多种权衡*:**线性同余引擎速度适中 ,对状态的存储要求非常小。 即使在没有高级算术指令集的处理器上 , 滞后的斐波纳契发生器也是非常快的 ,代价是更大的状态存储和有时不太理想的频谱特性。 梅森扭转器速度较慢,具有较高的状态存储要求,但正确的参数具有最长的非重复序列和最理想的光谱特性(对于给定的期望定义)。
所以,如果有一个更快的发电机的愿望,也许ranlux24_base或ranlux48_base比mt19937更好的select。
RAND()
如果你不得不使用rand()
那么关于如何生成浮点随机数的指南的C FAQ ? ,给我们一个类似于这样的例子来生成一个区间[0,1)
:
#include <stdlib.h> double randZeroToOne() { return rand() / (RAND_MAX + 1.); }
并在[M,N)
范围内生成一个随机数:
double randMToN(double M, double N) { return M + (rand() / ( RAND_MAX / (NM) ) ) ; }
老式的解决scheme,如:
double X=((double)rand()/(double)RAND_MAX);
应该符合所有的标准(便携式,标准和快速)。 显然生成的随机数必须接种标准程序是这样的:
srand((unsigned)time(NULL));
Boost随机库中的random_real类是您所需要的。
如果你使用C ++ TR1,你可以这样做。
如果速度是你最关心的问题,那么我就跟着去吧
double r = (double)rand() / (double)RAND_MAX;
C ++ 11标准库包含一个体面的框架和几个可用的生成器,这对于作业分配和非正式使用来说是完全足够的。
但是,对于生产级代码,您应该确切知道各种生成器的特定属性,然后再使用它们,因为它们都有警告。 此外,他们都没有通过像TestU01 PRNG的标准testing,除了ranlux发电机,如果使用慷慨的奢侈品因素。
如果你想要坚实的,可重复的结果,那么你必须带上自己的发电机。
如果你想要便携性,那么你必须带上自己的发电机。
如果你可以忍受限制的可移植性,那么你可以使用boost,或者C ++ 11框架和你自己的生成器一起使用。
更多的细节 – 包括一个简单而又快速的优秀质量和丰富的链接的代码 – 可以在我的答案中find类似的话题:
- 通用随机数生成
- 非常快速的均匀分布随机数发生器
对于专业统一的浮点偏差,还有两个问题需要考虑:
- 开放与半开放与封闭范围,即(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; }
( random_uint32()
和random_uint64()
是您的实际function的占位符,通常会作为模板parameter passing)
方法a演示了如何创build一个统一的偏差,这个偏差不会被过度的精度偏向于较低的值; 没有显示64位的代码,因为它比较简单,只需要屏蔽掉11位。 所有函数的分布是均匀的,但是如果没有这个技巧,那么在比其他地方更接近于0的区域将会有更多不同的值(由于变化的ulp,更精细的网格间距)。
方法c展示了在FPU只知道有符号的64位整数types的某些stream行平台上如何获得统一的偏离。 你最经常看到的是方法b,但是编译器必须在引擎盖下生成大量额外的代码来保留未签名的语义。
混合和匹配这些原则来创build自己定制的解决scheme。
所有这一切在JürgenDoornik的优秀论文“高周期随机数到浮点的转换”中都有解释。
首先包括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中定义的
正如我所看到的,有三种方法可以解决这个问题,
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; uf = 1.0; ui = ui | (custom_lcg(&internal_next) >> SHR_BIT); return uf - 1.0; }
无论select什么,function可以扩展如下,
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
引擎一样工作。
那么考虑简单和速度作为你的主要标准,你可以添加一个像这样的小型通用助手: –
// 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; }
随机数生成是一个研究得很好,复杂和先进的话题。 你可以在这里find一些简单但有用的algorithm,除了在其他答案中提到的algorithm:
永远Confuzzled
你可以试试Mersenne Twisteralgorithm。
http://en.wikipedia.org/wiki/Mersenne_twister
它具有速度和随机性以及GPL实现的良好结合。
这就是我为了我的需要而使用的:
int range_upper_bound = 12345; int random_number =((double)rand()/(double)range_upper_bound);
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);
快double X=((double)rand()/(double)RAND_MAX);
,但分配更好。 该algorithm只给出RAND_MAX均匀间隔的返回值select; 这个给RANDMAX ^ 6,所以它的分布只受到双精度的限制。
如果你想要一个长双重只需要添加几个迭代。 如果你需要[0,1]中的一个数字而不是[0,1],那么只需要把第4行读out = (double)rand()/(RAND_MAX);
。
//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; }
这应该做的伎俩,我用这个维基百科文章来帮助创build这个。 我相信它和drand48();
一样好drand48();