- 论坛徽章:
- 0
|
我做机器学习方面的研究,因此非常需要高质量的(伪)随机数生成器,下面这个是我参考"Numerical Recipes in C (NRIC)"和一些其它资料实现的一个C++ Wrapper for random number generator. 当然网上有很多类似的代码,不过我觉得这个应付常见的需求足够了,至少基本满足自己的需求。
它特点就是简单高效,但是随机数质量高,基于48位伪随机数生成引擎,实现了比较常见的binomial, cauchy, exponential, gamma(erlang), gaussian, uniform integers, uniform, poisson, 还有一个数学函数log(Gamma(x))。
因为参考了一些其它资料,我不知道该怎么发布这个东西,不过我有NRIC的正版授权,因此自己用应该是完全合法的。
使用的时候比较简单,比如需要生成200个正态分布的随机数:
- double x[200];
- Rand engine; // default to use current time as random seed
- for (int i = 0; i < 200; ++i)
- x[i] = engine.gasdev();
复制代码
// rand.h
- // -*- C++ -*-
- #ifndef RAND_H
- #define RAND_H
- #include <cmath>
- #include <cstdlib>
- class Rand {
- static const double PI;
- unsigned short xsubi[3];
- void set_seed(long seed = 0);
- public:
- Rand(long seed = 0) {set_seed(seed);}
- static double log_gamma(double xx); // returns log(Gamma(xx))
- // supported random deviates
- double bnldev(double pp, int n); // binomial
- double caudev(); // cauchy
- double caudev(double location, double scale);
- double expdev(); // exponential
- double expdev(double intensity);
- double gamdev(int shape); // gamma(erlang)
- double gamdev(int shape, double intensity);
- double gasdev(); // gaussian
- double gasdev(double mean, double std);
- int intdev(int end_point); // uniform integeters [0, end_point)
- int intdev(int start_point, int end_point);
- double poidev(double mean); // poisson
- double unidev(); // uniform [0, 1)
- double unidev(double start_point, double end_point);
- };
- inline double Rand::caudev()
- {
- return ::tan(PI * (unidev() - 0.5));
- }
- inline double Rand::caudev(double location, double scale)
- {
- return location + scale * caudev();
- }
- inline double Rand::expdev(double intensity)
- {
- return expdev() / intensity;
- }
- inline double Rand::gamdev(int shape, double intensity)
- {
- return gamdev(shape) / intensity;
- }
- inline double Rand::gasdev(double mean, double std)
- {
- return mean + std * gasdev();
- }
- inline int Rand::intdev(int end_point)
- {
- return static_cast<int>(::floor(unidev() * end_point));
- }
- inline int Rand::intdev(int start_point, int end_point)
- {
- return start_point + intdev(end_point - start_point);
- }
- inline double Rand::unidev()
- {
- return ::erand48(xsubi);
- }
- inline double Rand::unidev(double start_point, double end_point)
- {
- return start_point + (end_point - start_point) * unidev();
- }
- #endif
复制代码
// rand.cc
- #include "rand.h"
- #include <ctime>
- const double Rand::PI = 3.14159265358979324;
- void Rand::set_seed(long seed)
- {
- long rand_seed = seed;
- if (seed == 0)
- rand_seed = static_cast<long>(::time(0));
- for (int i = 0; i < 3; ++i) {
- xsubi[i] = rand_seed & 0xFFFF;
- rand_seed >>= 16;
- }
- // the first few numbers are somewhat related with the seed
- erand48(xsubi);
- erand48(xsubi);
- erand48(xsubi);
- erand48(xsubi);
- }
- double Rand::log_gamma(double xx)
- {
- double x, y, tmp, ser;
- static double cof[6] = {76.18009172947146,
- -86.50532032941677,
- 24.01409824083091,
- -1.231739572450155,
- 0.1208650973866179e-2,
- -0.5395239384953e-5};
- y = x = xx;
- tmp = x + 5.5;
- tmp -= (x + 0.5) * ::log(tmp);
- ser = 1.000000000190015;
- for (int j = 0; j <= 5; ++j)
- ser += cof[j] / ++y;
- return -tmp + ::log(2.5066282746310005 * ser / x);
- }
- double Rand::bnldev(double pp, int n)
- {
- int j;
- static int nold = -1;
- double am, em, g, angle, p, bnl, sq, t, y;
- static double pold = -1.0, pc, plog, pclog, en, oldg;
- p = (pp <= 0.5 ? pp : 1.0 - pp);
- am = n * p;
- if (n < 25) {
- bnl = 0.0;
- for (j = 1; j <= n; ++j)
- if (unidev() < p)
- ++bnl;
- } else if (am < 1.0) {
- g = ::exp(-am);
- t = 1.0;
- for (j = 0; j <= n; ++j) {
- t *= unidev();
- if (t < g)
- break;
- }
- bnl = (j <= n ? j : n);
- } else {
- if (n != nold) {
- en = n;
- oldg = log_gamma(en + 1.0);
- nold = n;
- } if (p != pold) {
- pc = 1.0 - p;
- plog = ::log(p);
- pclog = ::log(pc);
- pold = p;
- }
- sq = ::sqrt(2.0 * am * pc);
- do {
- do {
- angle = PI * unidev();
- y = ::tan(angle);
- em = sq * y + am;
- } while (em < 0.0 || em >= (en + 1.0));
- em = ::floor(em);
- t = 1.2 * sq * (1.0 + y * y) *
- ::exp(oldg - log_gamma(em + 1.0) -
- log_gamma(en - em + 1.0) +
- em * plog + (en - em) * pclog);
- } while (unidev() > t);
- bnl = em;
- }
- if (p != pp)
- bnl = n - bnl;
- return bnl;
- }
- double Rand::expdev()
- {
- double dum;
- do {
- dum = unidev();
- } while (dum == 0.0);
- return -::log(dum);
- }
- double Rand::gamdev(int shape)
- {
- double am, e, s, v1, v2, x, y;
- if (shape < 6) {
- x = 1.0;
- for (int j = 1; j <= shape; ++j)
- x *= unidev();
- x = -::log(x);
- } else {
- do {
- do {
- do {
- v1 = unidev();
- v2 = 2.0 * unidev() - 1.0;
- } while (v1 * v1 + v2 * v2 > 1.0);
- y = v2 / v1;
- am = shape - 1;
- s = sqrt(2.0 * am + 1.0);
- x = s * y + am;
- } while (x <= 0.0);
- e = (1.0 + y * y) * ::exp(am * ::log(x / am) - s * y);
- } while (unidev() > e);
- }
- return x;
- }
- double Rand::gasdev()
- {
- static int iset = 0;
- static double gset;
- double fac, rsq, v1, v2;
- if (iset == 0) {
- do {
- v1 = 2.0 * unidev() - 1.0;
- v2 = 2.0 * unidev() - 1.0;
- rsq = v1 * v1 + v2 * v2;
- } while (rsq >= 1.0 || rsq == 0.0);
- fac = sqrt(-2.0 * ::log(rsq) / rsq);
- gset = v1 * fac;
- iset = 1;
- return v2 * fac;
- }
- iset = 0;
- return gset;
- }
- double Rand::poidev(double mean)
- {
- static double sq, alxm, g, oldm = -1.0;
- double em, t, y;
- if (mean < 12.0) {
- if (mean != oldm) {
- oldm = mean;
- g = ::exp(-mean);
- }
- em = -1;
- t = 1.0;
- do {
- ++em;
- t *= unidev();
- } while (t > g);
- } else {
- if (mean != oldm) {
- oldm = mean;
- sq = sqrt(2.0 * mean);
- alxm = ::log(mean);
- g = mean * alxm - log_gamma(mean + 1.0);
- }
- do {
- do {
- y = ::tan(PI * unidev());
- em = sq * y + mean;
- } while (em < 0.0);
- em = ::floor(em);
- t = 0.9 * (1.0 + y * y) *
- ::exp(em * alxm - log_gamma(em + 1.0) - g);
- } while (unidev() > t);
- }
- return em;
- }
复制代码 |
|