本文不對具體的實現(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吧。