GitHub: https://github.com/storagezhang
Emai: debugzhang@163.com
華為雲社群: https://bbs.huaweicloud.com/blogs/249894
LevelDB: https://github.com/google/leveldb
C 語言中偽隨機數生成演算法實際上是採用了"線性同餘法":
\(seed = (seed * A + C ) \% M\)
其中 \(A,C,M\) 都是常數(一般會取質數)。當 \(C=0\) 時,叫做乘同餘法。
假設定義隨機數函式
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\):
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<uint32_t>((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
語句:
if (seed_ > M) {
seed_ -= M;
}
由於 \(H\) 和 \(L\) 都小於 \(M\),故 \(H+M<2L\)。
經過語句,等式右邊也等於 \((H + L) \%\ M\) 了。
綜上,等式成立