1. 程式人生 > >LevelDB 原始碼解析之 Random 隨機數

LevelDB 原始碼解析之 Random 隨機數

> GitHub: https://github.com/storagezhang > > Emai: [email protected] > > 華為雲社群: https://bbs.huaweicloud.com/blogs/249894 > > LevelDB: https://github.com/google/leveldb C 語言中偽隨機數生成演算法實際上是採用了"線性同餘法": $seed = (seed * A + C ) \% M$ 其中 $A,C,M$ 都是常數(一般會取質數)。當 $C=0$ 時,叫做乘同餘法。 假設定義隨機數函式 ```c++ void rand(int &seed) { seed = (seed * A + C ) % M; } ``` 每次呼叫 `rand` 函式都會產生一個隨機值賦值給 `seed`,實際上 `rand` 函式生成的隨機數是一個遞推序列,初值為 `seed`。所以當初始的 `seed` 相同時,得到的遞推序列也會相同。我們稱 `seed` 為隨機數種子,稱 `rand` 生成的隨機數為偽隨機數,一個偽隨機數常用的原則就是 `M` 儘可能的大。 在 LevelDB 的隨機數類 `Random` 類中,$A=16807, M=2147483647, C=0$: ```c++ explicit Random(uint32_t s) : seed_(s & 0x7fffffffu) { // Avoid bad seeds. if (seed_ == 0 || seed_ == 2147483647L) { seed_ = 1; } } uint32_t Next() { static const uint32_t M = 2147483647L; // 2^31-1 static const uint64_t A = 16807; // bits 14, 8, 7, 5, 2, 1, 0 // We are computing // seed_ = (seed_ * A) % M, where M = 2^31-1 // // seed_ must not be zero or M, or else all subsequent computed values // will be zero or M respectively. For all other values, seed_ will end // up cycling through every number in [1,M-1] uint64_t product = seed_ * A; // Compute (product % M) using the fact that ((x << 31) % M) == x. seed_ = static_cast((product >> 31) + (product & M)); // The first reduction may overflow by 1 bit, so we may need to // repeat. mod == M is not possible; using > allows the faster // sign-bit-based test. if (seed_ > M) { seed_ -= M; } return seed_; } ``` 原始碼中利用 `(product >> 31) + (product & M)` 來代替 `product % M`,主要是為了避免 64 位除法。 下面證明 $product\ \%\ M = (product >> 31) + (product\ \&\ M)$: $$ \begin{align} &將\ product\ 分為高\ 33\ 位和低\ 31\ 位 \\ \\ &令高\ 33\ 位的值為\ H,低\ 31\ 位的值為\ L \\ \\ &則\ product = H << 31 + L = H \cdot 2^{31}+L = H \cdot M + L \\ \\ &因為\ product = seed \cdot A, 且\ seed\ 和\ A\ 都小於\ M,故\ H\ 必小於\ M \\ \\ &等式左邊 = product \%\ M = (H \cdot M+L) \%\ M = (H + L) \%\ M \\ \\ &等式右邊 = (product >> 31) + (product\ \&\ M) = (H \cdot 2^{31}+L)>>31 + L = H + L \\ \end{align} $$ 此時考慮下方的 `if` 語句: ```c++ if (seed_ > M) { seed_ -= M; } ``` 由於 $H$ 和 $L$ 都小於 $M$,故 $H+M<2L$。 經過語句,等式右邊也等於 $(H + L) \%\ M$ 了。 綜上,等