golang源碼學習之math/rand

本文不對具體的實現(xiàn)作分析,而是為了解決下面兩個問題。
  • 相同種子,為什么每次運行的結(jié)果一樣?
  • 不同的種子, 為什么每次運行的結(jié)果有可能一樣?
從下面兩句代碼開始分析吧
    rand.Seed(10)
    rand.Int()
// math/rand/rand.go

func Seed(seed int64) { globalRand.Seed(seed) }

// globalRand 全局變量
var globalRand = New(&lockedSource{src: NewSource(1).(Source64)})

func NewSource(seed int64) Source {
    var rng rngSource
    //最終Seed的是 rngSource
    rng.Seed(seed) 
    return &rng
}

Seed
// math/rand/rng.go
func (rng *rngSource) Seed(seed int64) {
    rng.tap = 0
    rng.feed = rngLen - rngTap

    // seed 為in64,而int32max為 (1 << 31) - 1, seed的取值范圍遠遠比int32max大,所以取模之后會得到一些相同的seed
    seed = seed % int32max
    if seed < 0 {
        seed += int32max
    }
    if seed == 0 {
        seed = 89482311
    }

    x := int32(seed)
    // vec是個緩存數(shù)組, 初始化vec
    // 下面的計算都是寫死的,不存在隨機性,所以相同的seed得到的vec也是相同的
    for i := -20; i < rngLen; i++ {
        x = seedrand(x)
        if i >= 0 {
            var u int64
            u = int64(x) << 40
            x = seedrand(x)
            u ^= int64(x) << 20
            x = seedrand(x)
            u ^= int64(x)
            u ^= rngCooked[i]
            rng.vec[i] = u
        }
    }
}

不同的seed通過取??赡艿玫较嗤膕eed。相同的seed得到的初始化的vec也是相同的。
這是似乎快解答上面兩個問題了。繼續(xù)往下看,看看隨機數(shù)是如何獲取的

//math/rand/rand.go
func (r *Rand) Int() int {
    u := uint(r.Int63())
    return int(u << 1 >> 1) // clear sign bit if int == int32
}

func (r *Rand) Int63() int64 { 
    // 這里的r.src其實就是上面的rngSource,最終執(zhí)行的是rngSource里面的Int63()
    return r.src.Int63() 
}

Int63
// math/rand/rng.go
func (rng *rngSource) Int63() int64 {
    return int64(rng.Uint64() & rngMask)
}

func (rng *rngSource) Uint64() uint64 {

    // tap和feed在前面的代碼中也未作修改, 所以在下面的計算中每次得到的tap和feed都是一樣的。
    rng.tap--
    if rng.tap < 0 {
        rng.tap += rngLen
    }

    rng.feed--
    if rng.feed < 0 {
        rng.feed += rngLen
    }

    // 根據(jù)feed和tap兩個索引在vec中取值
    x := rng.vec[rng.feed] + rng.vec[rng.tap]
    // 修改feed對應(yīng)的值
    rng.vec[rng.feed] = x
    return uint64(x)
}

在vec相同的情況下,每次運行得到的結(jié)果都是一樣的。再綜合上面的結(jié)論可以解答上面的兩個問題了。
其實不光rand.Int()會調(diào)用Int63,Intn、Uint32、Int31、Int63n也會調(diào)用Int63。

總結(jié):math/rand是個偽隨機數(shù),如果想獲取真正的隨機數(shù)那么還是用crypto/rand吧。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容