GoLang 中的隨機數 tips

根號三發表於2018-03-20

隨機數我們都知道,就是計算機通過某種演算法,“隨機”的生成一個數字。很多程式語言都有內建的方法來生成隨機數,那麼 GoLang 中是怎樣一種情況呢?

偽隨機數

我們都知道“隨機數”在現實生活中的概念,可能你隨手拋一個硬幣,就可以說其結果是隨機的,但是在計算機中要確定一個“隨機數”真的是“隨機數”,那可是有標準的,不是你隨隨便便說是就是。

根據密碼學原理,要想對一個“隨機數”進行隨機性檢驗有以下幾個標準:

  • 統計學偽隨機性 - 在給定的隨機位元流樣本中,1 的數量大致等於 0 的數量,也就是說,“10”“01”“00”“11” 四者數量大致相等。說人話就是:“一眼看上去是隨機的”。
  • 密碼學安全偽隨機性 - 就是給定隨機樣本的一部分和隨機演算法,不能有效的演算出隨機樣本的剩餘部分。
  • 真隨機性 - 其定義為隨機樣本不可重現。

根據以上幾個標準,其對應的隨機數也就分為以下幾類:

  • 偽隨機數 - 滿足第一個條件的隨機數。
  • 密碼學安全的偽隨機數 - 同時滿足前兩個條件的隨機數。可以通過密碼學安全偽隨機數生成器計算得出。
  • 真隨機數 -同時滿足三個條件的隨機數。

瞭解了以上幾個概念,我們就知道了“偽隨機數”其實就是一個“看似隨機,實則並不真正隨機”的數字。

偽隨機數生成器

在實際應用中大部分情況下偽隨機數就足夠了。這些數列是“似乎”隨機的數,實際上它們是通過一個固定的、可以重複的計算方法產生的。因為它們實際上是可以計算出來的,所以它們並不真正地隨機,但是它們具有類似於隨機數的統計特徵。產生這樣的結果的生成器我們叫做偽隨機數生成器。

一般只有在密碼學場景中,我們才需要使用“真隨機數”。

在大部分程式語言中,提供的都是“偽隨機數生成器”,例如 JS 中的 Math.random()GoLang 中的 math/rand 包。

GoLang 中的偽隨機數

GoLang 中,我們可以通過 math/rand 包裡的方法來生成一個偽隨機數:

package main

import (
  "fmt"
  "math/rand"
)

func main() {
  fmt.Println(rand.Int())   // => 134020434
}
複製程式碼

上面的程式碼中,我們通過 rand.Int() 方法來生成一個偽隨機數。看起來好像沒什麼問題嘛,人家也很 OK 啦。

但是細心的你會發現,你在自己電腦上執行上面的程式碼竟然和我的一樣。無論你怎麼執行,它都一樣。

我們知道 JS 中的 Math.random() 每次都會返回一個不一樣的數字,但是 GoLang 中的偽隨機數生成器預設情況下竟然會返回相同的數值,這還不反了天了?

都是偽隨機數生成器,為什麼差別就這麼大呢?這裡我們就要了解一下“隨機種子”的概念啦。

隨機種子

我們知道,偽隨機數,是使用一個確定性的演算法計算出來的似乎是隨機的數序,因此偽隨機數實際上並不隨機。

那麼自然,在計算偽隨機數時假如使用的開始值不變的話,那麼演算法計算出的偽隨機數的數序自然也是不變的咯。

這個“開始值”,就被稱為隨機種子。

查閱文件,我們得知,Int() 函式是從 default Source(預設源)中產生的偽隨機數。

而這個 default Source,我們從 Seed 部分可以看到,如果你沒有設定隨機種子,那麼預設初始種子總是從 1 開始。

既然隨機種子一樣,那自然其結果也是一樣的。

隨機的偽隨機數

我們已經知道了預設隨機種子是從 1 開始,那麼我們只要在每次生成隨機數之前先設定一個不一樣的種子,那麼其結果自然也就不一樣了。

我們要儘可能保證每次偽隨機數生成器工作時使用的是不同的種子,通常的做法是採用當前時間作為種子。

package main

import (
  "fmt"
  "math/rand"
  "time"
)

func main() {
  rand.Seed(int64(time.Now().UnixNano()))

  fmt.Println(rand.Int())
}
複製程式碼

這樣,由於種子不同,我們每次執行的結果也就不一樣。我們就能達到獲取偽隨機數的目的啦。

真隨機數

如果我們的應用對安全性要求比較高,需要使用真隨機數的話,那麼可以使用 crypto/rand 包中的方法。

package main

import (
  "crypto/rand"
  "fmt"
  "math/big"
)

func main() {
  // 生成 20 個 [0, 100) 範圍的真隨機數。
  for i := 0; i < 20; i++ {
    result, _ := rand.Int(rand.Reader, big.NewInt(100))
    fmt.Println(result)
  }
}
複製程式碼

上面的程式每次執行的結果都是不一樣的,會真正隨機的生成隨機數。


訪問 github.com/sqrthree/sq… 閱讀更多文章。

相關文章