如今大部分编译器的随机数算法还是线性同余算法,简称LCG。
线性同余算法(LCG):http://en.wikipedia.org/wiki/Linear_congruential_generator
A Linear Congruential Generator (LCG) represents one of the oldest and best-known pseudorandomnumbergenerator algorithms.
The generator is defined by the recurrencerelation:
where is the sequence of pseudorandom values, and
- — the "modulus"
- — the "multiplier"
- — the "increment"
- — the "seed" or "start value"
are integer constants that specify the generator. If c = 0, the generator is often called a multiplicative congruential method, or LehmerRNG. If c ≠ 0, the generator is called a mixed congruential method.
The period of a general LCG is at most m, and for some choices of a much less than that. Provided that c is nonzero, the LCG will have a full period for all seed values if and only if:
- and are relatively prime,
- is divisible by all prime factors of ,
- is a multiple of 4 if is a multiple of 4.
These three requirements are referred to as the Hull-Dobell Theorem. While LCGs are capable of producing decent pseudorandom numbers, this is extremely sensitive to the choice of the parameters c, m, and a.
Historically, poor choices had led to ineffective implementations of LCGs. A particularly illustrative example of this is RANDU which was widely used in the early 1970s and led to many results which are currently being questioned because of the use of this poor LCG.
The most efficient LCGs have an m equal to a power of 2, most often m = 232 or m = 264, because this allows the modulus operation to be computed by merely truncating all but the rightmost 32 or 64 bits.
大致对应的实现代码:
static unsigned int mySeed = 1; void mySrand(unsigned int seed) { mySeed = seed; } unsigned int myRand(){ mySeed = mySeed * 214013 + 2531011; return (unsigned int)((mySeed >> 16) & 0x7fff); }
上面的代码中,a = 214013, c = 2531011, m = 0x1ffffffff, 在32bit系统中,由于unsigned int为4字节,不可能超过m,因此程序的取模操作省略了。
mySrand传入的参数即为初始种子,每次调用myRand会取得一个随机数,且调用myRand产生的随机数取决于之前的mySeed值,mySeed一直在变,故随机数也随之改变。
因此,srand(unsigned int)只需要调用一次就可以了。假如每次调用rand()前都调用一次相同的srand,那么产生的随机数必然也是一样的。
初始种子一般这样调用srand((unsigned int)time(NULL)),如此,不同的时间调用srand就会得到不同的随机数。
更好的随机数算法,Mersenne twister,RNG,Python和Ruby都采用它作为默认的随机数生成算法。
梅森旋转算法Mersenne twisterhttp://en.wikipedia.org/wiki/Mersenne_twister
没搞错的话,下面的算法是C++之父写的(C++程序设计语言 特别版):
#include <map> #include <iostream> class Randint { // 均匀分布,假定32位long unsigned long randx; public: Randint(long s = 0) { randx = s; } void seed(long s) { randx = s; } // 魔幻数选用32位long中的31位 long abs(long x) { return x&0x7fffffff; } static double max() { return 2147483648.0; } // 注意:double long draw() { return randx = randx * 1103515245; } double fdraw() { return abs(draw()) / max(); } // 在区间[0, 1] long operator() () { return abs(draw()); } // 在区间[0, pow(2, 31)] }; class Urand : public Randint { // 均匀分布,区间[0:n] long n; public: Urand(long nn) { n = nn; } long operator() () { long r = n * fdraw(); return (r == n) ? n-1 : r; } }; class Erand : public Randint { // 指数分布随机数生成器 long mean; public: Erand(long m) { mean = m; } long operator() () { return -mean * long( (max() - draw()) / max() + .5); } }; int main () { const int count = 10; Urand temp(count); temp.seed(time(NULL)); std::map<int, int>bucket; for (int i = 0; i < 1000000; ++i) { bucket[temp()]++; } for (int i = 0; i < count; ++i) { std::cout << bucket[i] << '\n'; } }