LevelDB 源碼解析之 Random 隨機數

GitHub: //github.com/storagezhang

Emai: [email protected]

華為雲社區: //bbs.huaweicloud.com/blogs/249894

LevelDB: //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\) 了。

綜上,等式成立

Tags: