我們講的隨機數其實暗指偽隨機數。提及隨機數,不少朋友可能想到C語言的庫函數rand(),rand()隨機性太差,速度太慢。
古老的LCG(linear congruential generator)代表了最好的偽隨機數產生器算法。主要原因是容易理解,容易實現,而且速度快。這種算法數學上基于X(n+1) = (a * X(n) + c) % m這樣的公式,其中:
模m, m > 0
系數a, 0 < a < m
增量c, 0 <= c < m
原始值(種子) 0 <= X(0) < m
其中參數c, m, a比較敏感,或者說直接影響了偽隨機數產生的質量。
一般而言,高LCG的m是2的指數次冪(一般2^32或者2^64),因為這樣取模操作截斷最右的32或64位就可以了。多數編譯器的庫中使用了該理論實現其偽隨機數發生器rand()。下面是部分編譯器使用的各個參數值:
Source                           m            a            c          rand() / Random(L)的種子位
Numerical Recipes                  
                                
2^32         1664525      1013904223    
Borland C
/C++                      
                                
2^32         22695477     1          位30..16 in rand(), 30..0 in lrand()
glibc (used by GCC)                
                                
2^32         1103515245   12345      位30..0
ANSI C: Watcom, Digital Mars, CodeWarrior, IBM VisualAge C
/C++
                                
2^32         1103515245   12345      位30..16
Borland Delphi, Virtual Pascal
                                
2^32         134775813    1          位63..32 of (seed * L)
Microsoft Visual
/Quick C/C++
                                
2^32         214013       2531011    位30..16
Apple CarbonLib
                                
2^31-1       16807        0          見Park–Miller隨機數發生器

LCG不能用于隨機數要求高的場合,例如不能用于Monte Carlo模擬,不能用于加密應用。
LCG有一些嚴重的缺陷,例如如果LCG用做N維空間的點坐標,這些點最多位于m1/n超平面上(Marsaglia定理),這是由于產生的相繼X(n)值的關聯所致。
另外一個問題就是如果m設置為2的指數,產生的低位序列周期遠遠小于整體。
一般而言,輸出序列的基數b中最低n位,bk = m (k是某個整數),最大周期bn.
有些場合LCG有很好的應用,例如內存很緊張的嵌入式中,電子游戲控制臺用的小整數,使用高位可以勝任。
LCG的一種實現版本如下:
http://www.shnenglu.com/Chipset/archive/2008/12/20/69918.html
如果需要高質量的偽隨機數,內存充足(約2kb),Mersenne twister算法是個不錯的選擇。Mersenne twister產生隨機數的質量幾乎超過任何LCG。不過一般Mersenne twister的實現使用LCG產生種子。
Mersenne twister是Makoto Matsumoto (松本)和Takuji Nishimura (西村)于1997年開發的偽隨機數產生器,基于有限二進制字段上的矩陣線性再生??梢钥焖佼a生高質量的偽隨機數,修正了古老隨機數產生算法的很多缺陷。Mersenne twister這個名字來自周期長度通常取Mersenne質數這樣一個事實。常見的有兩個變種Mersenne Twister MT19937和Mersenne Twister MT19937-64。
Mersenne Twister有很多長處,例如:周期2^19937 - 1對于一般的應用來說,足夠大了,序列關聯比較小,能通過很多隨機性測試。
關于Mersenne Twister比較詳細的論述請參閱http://www.shnenglu.com/Chipset/archive/2009/01/19/72330.html
一種實現版本如下:
  1 //************************************************************************
  2 //  This is a slightly modified version of Equamen mersenne twister.
  3 //
  4 //  Copyright (C) 2009 Chipset
  5 //
  6 //  This program is free software: you can redistribute it and/or modify
  7 //  it under the terms of the GNU Affero General Public License as
  8 //  published by the Free Software Foundation, either version 3 of the
  9 //  License, or (at your option) any later version.
 10 //
 11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
 12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 13 //  GNU Affero General Public License for more details.
 14 //
 15 //  You should have received a copy of the GNU Affero General Public License
 16 //  along with this program. If not, see <http://www.gnu.org/licenses/>.
 17 //************************************************************************
 18 
 19 // Original Coyright (c) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura
 20 //
 21 // Functions for MT19937, with initialization improved 2002/2/10.
 22 // Coded by Takuji Nishimura and Makoto Matsumoto.
 23 // This is a faster version by taking Shawn Cokus's optimization,
 24 // Matthe Bellew's simplification, Isaku Wada's real version.
 25 // C++ version by Lyell Haynes (Equamen)
 26 //
 27 // Redistribution and use in source and binary forms, with or without
 28 // modification, are permitted provided that the following conditions
 29 // are met:
 30 //
 31 // 1. Redistributions of source code must retain the above copyright
 32 //    notice, this list of conditions and the following disclaimer.
 33 //
 34 // 2. Redistributions in binary form must reproduce the above copyright
 35 //    notice, this list of conditions and the following disclaimer in the
 36 //    documentation and/or other materials provided with the distribution.
 37 //
 38 // 3. The names of its contributors may not be used to endorse or promote
 39 //    products derived from this software without specific prior written
 40 //    permission.
 41 //
 42 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 43 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 44 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 45 // A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 46 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 47 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 48 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 49 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 50 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 51 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 52 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 53 //
 54 
 55 #ifndef mtrandom_HPP_
 56 #define mtrandom_HPP_
 57 
 58 #include <stddef.h>
 59 
 60 class mtrandom
 61 {
 62 public:
 63     mtrandom() : left(1) { init(); }
 64 
 65     explicit mtrandom(size_t seed) : left(1) {    init(seed);    }
 66 
 67     mtrandom(size_t* init_key, int key_length) : left(1)
 68     {
 69         int i = 1, j = 0;
 70         int k = N > key_length ? N : key_length;
 71         init();
 72         for(; k; --k)
 73         {
 74             state[i]  =  (state[i] ^ ((state[i - 1^ (state[i - 1>> 30)) * 1664525UL))+ init_key[j] + j; // non linear
 75             state[i] &=  4294967295UL// for WORDSIZE > 32 machines
 76             ++i;
 77             ++j;
 78             if(i >= N)
 79             {
 80                 state[0= state[N - 1];
 81                 i = 1;
 82             }
 83             if(j >= key_length)
 84                 j = 0;
 85         }
 86 
 87         for(k = N - 1; k; --k)
 88         {
 89             state[i] = (state[i] ^ ((state[i - 1^ (state[i - 1>> 30)) * 1566083941UL)) - i; // non linear
 90             state[i] &= 4294967295UL// for WORDSIZE > 32 machines
 91             ++i;
 92             if(i >= N)
 93             {
 94                 state[0= state[N - 1];
 95                 i = 1;
 96             }
 97         }
 98 
 99         state[0]  =  2147483648UL// MSB is 1; assuring non-zero initial array
100     }
101 
102     void reset(size_t rs)
103     {
104         init(rs);
105         next_state();
106     }
107 
108     size_t rand()
109     {
110         size_t y;
111         if(0 == --left)
112             next_state();
113         y  = *next++;
114         // Tempering
115         y ^= (y >> 11);
116         y ^= (y << 7& 0x9d2c5680UL;
117         y ^= (y << 15& 0xefc60000UL;
118         y ^= (y >> 18);
119         return y;
120     }
121 
122     double real()    {    return (double)rand() / -1UL;    }
123 
124         // generates a random number on [0,1) with 53-bit resolution
125     double res53()
126     {
127         size_t a = rand() >> 5, b = rand() >> 6;
128         return (a * 67108864.0 + b) / 9007199254740992.0;
129     }
130 
131 private:
132     void init(size_t seed = 19650218UL)
133     {
134         state[0=  seed & 4294967295UL;
135         for(int j = 1; j < N; ++j)
136         {
137             state[j]  =  (1812433253UL * (state[j - 1^ (state[j - 1]  >>  30)) + j);
138             // See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier.
139             // In the previous versions, MSBs of the seed affect
140             // only MSBs of the array state[].
141             // 2002/01/09 modified by Makoto Matsumoto
142             state[j] &=  4294967295UL;  // for >32 bit machines
143         }
144     }
145 
146     void next_state()
147     {
148         size_t* p = state;
149         int i;
150 
151         for(i = N - M + 1--i; ++p)
152             *= (p[M] ^ twist(p[0], p[1]));
153 
154         for(i = M; --i; ++p)
155             *= (p[M - N] ^ twist(p[0], p[1]));
156         *= p[M - N] ^ twist(p[0], state[0]);
157         left = N;
158         next = state;
159     }
160 
161     size_t mixbits(size_t u, size_t v) const
162     {
163         return (u & 2147483648UL| (v & 2147483647UL);
164     }
165 
166     size_t twist(size_t u, size_t v) const
167     {
168         return ((mixbits(u, v)  >>  1^ (v & 1UL ? 2567483615UL : 0UL));
169     }
170 
171     static const int N = 624, M = 397;
172     size_t state[N];
173     size_t left;
174     size_t* next;
175 };
176 
177 class mtrand_help
178 {
179   static mtrandom r;
180 public:
181   mtrand_help() {}
182   void operator()(size_t s) { r.reset(s); }
183   size_t operator()() const { return r.rand(); }
184   double operator()(double) { return r.real(); }
185 };
186 mtrandom mtrand_help:: r;
187 
188 extern void mtsrand(size_t s) { mtrand_help()(s); }
189 extern size_t mtirand() { return mtrand_help()(); }
190 extern double mtdrand() { return mtrand_help()(1.0); }
191 
192 #endif // mtrandom_HPP_
193