最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

解決Go中使用seed得到相同隨機數(shù)的問題

 更新時間:2019年10月09日 08:25:33   作者:detectiveHLH  
這篇文章主要介紹了Go中使用seed得到相同隨機數(shù)的問題,需要的朋友可以參考下

1. 重復的隨機數(shù)

廢話不多說,首先我們來看使用seed的一個很神奇的現(xiàn)象。

func main() {
  for i := 0; i < 5; i++ {
  rand.Seed(time.Now().Unix())
    fmt.Println(rand.Intn(100))
  }
}

// 結果如下
// 90
// 90
// 90
// 90
// 90

可能不熟悉seed用法的看到這里會很疑惑,我不是都用了seed嗎?為何我隨機出來的數(shù)字都是一樣的?不應該每次都不一樣嗎?

可能會有人說是你數(shù)據(jù)的樣本空間太小了,OK,我們加大樣本空間到10w再試試。

func main() {
  for i := 0; i < 5; i++ {
  rand.Seed(time.Now().Unix())
    fmt.Println(rand.Intn(100000))
  }
}

// 結果如下
// 84077
// 84077
// 84077
// 84077
// 84077

你會發(fā)現(xiàn)結果仍然是一樣的。簡單的推理一下我們就能知道,在上面那種情況,每次都取到相同的隨機數(shù)跟我們所取的樣本空間大小是無關的。那么唯一有關的就是seed。我們首先得明確seed的用途。

2. seed的用途

在這里就不賣關子了,先給出結論。

上面每次得到相同隨機數(shù)是因為在上面的循環(huán)中,每次操作的間隔都在毫秒級下,所以每次通過time.Now().Unix()取出來的時間戳都是同一個值,換句話說就是使用了同一個seed。

這個其實很好驗證。只需要在每次循環(huán)的時候將生成的時間戳打印出來,你就會發(fā)現(xiàn)每次打印出來的時間戳都是一樣的。

每次rand都會使用相同的seed來生成隨機隊列,這樣一來在循環(huán)中使用相同seed得到的隨機隊列都是相同的,而生成隨機數(shù)時每次都會去取同一個位置的數(shù),所以每次取到的隨機數(shù)都是相同的。

seed 只用于決定一個確定的隨機序列。不管seed多大多小,只要隨機序列一確定,本身就不會再重復。除非是樣本空間太小。解決方案有兩種:

在全局初始化調用一次seed即可
每次使用納秒級別的種子(強烈不推薦這種)

3. 不用每次調用

上面的解決方案建議各位不要使用第二種,給出是因為在某種情況下的確可以解決問題。比如在你的服務中使用這個seed的地方是串行的,那么每次得到的隨機序列的確會不一樣。

但是如果在高并發(fā)下呢?你能夠保證每次取到的還是不一樣的嗎?事實證明,在高并發(fā)下,即使使用UnixNano作為解決方案,同樣會得到相同的時間戳,Go官方也不建議在服務中同時調用。

Seed should not be called concurrently with any other Rand method.

接下來會帶大家了解一下代碼的細節(jié)。想了解源碼的可以繼續(xù)讀下去。

4. 源碼解析-seed

4.1 seed

首先來看一下seed做了什么。

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

  seed = seed % int32max
  if seed < 0 { // 如果是負數(shù),則強行轉換為一個int32的整數(shù)
    seed += int32max
  }
  if seed == 0 { // 如果seed沒有被賦值,則默認給一個值
    seed = 89482311
  }

  x := int32(seed)
  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賦值了兩個定義好的變量,rng.tap和rng.feed。rngLen和rngTap是兩個常量。我們來看一下相關的常量定義。

const (
  rngLen  = 607
  rngTap  = 273
  rngMax  = 1 << 63
  rngMask = rngMax - 1
  int32max = (1 << 31) - 1
)

由此可見,無論seed是否相同,這兩個變量的值都不會受seed的影響。同時,seed的值會最終決定x的值,只要seed相同,則得到的x就相同。而且無論seed是否被賦值,只要檢測到是零值,都會默認的賦值為89482311。

接下來我們再看seedrand。

4.2 seedrand

// seed rng x[n+1] = 48271 * x[n] mod (2**31 - 1)
func seedrand(x int32) int32 {
  const (
    A = 48271
    Q = 44488
    R = 3399
  )

  hi := x / Q    // 取除數(shù)
  lo := x % Q    // 取余數(shù)
  x = A*lo - R*hi // 通過公式重新給x賦值
  if x < 0 {
    x += int32max // 如果x是負數(shù),則強行轉換為一個int32的正整數(shù)
  }
  return x
}

可以看出,只要傳入的x相同,則最后輸出的x一定相同。進而最后得到的隨機序列rng.vec就相同。

到此我們驗證我們最開始給出的結論,即只要每次傳入的seed相同,則生成的隨機序列就相同。驗證了這個之后我們再繼續(xù)驗證為什么每次取到的隨機序列的值都是相同的。

5. 源碼解析-Intn

首先舉個例子,來直觀的描述上面提到的問題。

func printRandom() {
 for i := 0; i < 2; i++ {
  fmt.Println(rand.Intn(100))
 }
}

// 結果
// 81
// 87
// 81
// 87

假設printRandom是一個單獨的Go文件,那么你無論run多少次,每次打印出來的隨機序列都是一樣的。通過閱讀seed的源碼我們知道,這是因為生成了相同的隨機序列。那么為什么會每次都取到同樣的值呢?不說廢話,我們一層一層來看。

5.1 Intn

func (r *Rand) Intn(n int) int {
  if n <= 0 {
    panic("invalid argument to Intn")
  }
  if n <= 1<<31-1 {
    return int(r.Int31n(int32(n)))
  }
  return int(r.Int63n(int64(n)))
}

可以看到,如果n小于等于0,就會直接panic。其次,會根據(jù)傳入的數(shù)據(jù)類型,返回對應的類型。

雖然說這里調用分成了Int31n和Int63n,但是往下看的你會發(fā)現(xiàn),其實都是調用的r.Int63(),只不過在返回64位的時候做了一個右移的操作。

// r.Int31n的調用
func (r *Rand) Int31() int32 { return int32(r.Int63() >> 32) }

// r.Int63n的調用
func (r *Rand) Int63() int64 { return r.src.Int63() }

5.2 Int63

先給出這個函數(shù)的相關代碼。

// 返回一個非負的int64偽隨機數(shù).
func (rng *rngSource) Int63() int64 {
  return int64(rng.Uint64() & rngMask)
}

func (rng *rngSource) Uint64() uint64 {
  rng.tap--
  if rng.tap < 0 {
    rng.tap += rngLen
  }

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

  x := rng.vec[rng.feed] + rng.vec[rng.tap]
  rng.vec[rng.feed] = x
  return uint64(x)
}

可以看到,無論是int31還是int63,最終都會進入Uint64這個函數(shù)中。而在這兩個函數(shù)中,這兩個變量的值顯得尤為關鍵。因為直接決定了最后得到的隨機數(shù),這兩個變量的賦值如下。

rng.tap = 0
rng.feed = rngLen - rngTap

tap的值是常量0,而feed的值決定于rngLen和rngTap,而這兩個變量的值也是一個常量。如此,每次從隨機隊列中取到的值都是確定的兩個值的和。

到這,我們也驗證了只要傳入的seed相同,并且每次都調用seed方法,那么每次隨機出來的值一定是相同的。

6. 結論

首先評估是否需要使用seed,其次,使用seed只需要在全局調用一次即可,如果多次調用則有可能取到相同隨機數(shù)。

總結

以上所述是小編給大家介紹的解決Go中使用seed得到相同隨機數(shù)的問題,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
如果你覺得本文對你有幫助,歡迎轉載,煩請注明出處,謝謝!

相關文章

  • Go?處理大數(shù)組使用?for?range?和?for?循環(huán)的區(qū)別

    Go?處理大數(shù)組使用?for?range?和?for?循環(huán)的區(qū)別

    這篇文章主要介紹了Go處理大數(shù)組使用for?range和for循環(huán)的區(qū)別,對于遍歷大數(shù)組而言,for循環(huán)能比for?range循環(huán)更高效與穩(wěn)定,這一點在數(shù)組元素為結構體類型更加明顯,下文具體分析感興趣得小伙伴可以參考一下
    2022-05-05
  • 基于go-cqhttp與Flask搭建定制機器人項目實戰(zhàn)示例

    基于go-cqhttp與Flask搭建定制機器人項目實戰(zhàn)示例

    這篇文章主要為大家介紹了基于go-cqhttp與Flask搭建定制機器人項目實戰(zhàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-11-11
  • Goland 斷點調試Debug的操作

    Goland 斷點調試Debug的操作

    這篇文章主要介紹了Goland 斷點調試Debug的操作方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • golang panic 函數(shù)用法示例詳解

    golang panic 函數(shù)用法示例詳解

    在Go語言中,panic用于觸發(fā)不可恢復的錯誤,終止函數(shù)執(zhí)行并逐層向上觸發(fā)defer,最終若未被recover捕獲,程序會崩潰,recover用于在defer函數(shù)中捕獲panic,恢復程序流程,建議優(yōu)先返回error,僅在嚴重錯誤或不可恢復場景下使用panic,并在關鍵位置recover,感興趣的朋友一起看看吧
    2025-03-03
  • Go實現(xiàn)MD5加密的三種方法小結

    Go實現(xiàn)MD5加密的三種方法小結

    本文主要介紹了Go實現(xiàn)MD5加密的三種方法小結,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-03-03
  • go cron定時任務的基本使用講解

    go cron定時任務的基本使用講解

    這篇文章主要為大家介紹了gocron定時任務的基本使用講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-06-06
  • Go語言異常處理案例解析

    Go語言異常處理案例解析

    這篇文章主要介紹了Go語言異常處理案例解析,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下
    2021-07-07
  • Go語言中Gin框架使用JWT實現(xiàn)登錄認證的方案

    Go語言中Gin框架使用JWT實現(xiàn)登錄認證的方案

    在如今前后端分離開發(fā)的大環(huán)境中,我們需要解決一些登陸,后期身份認證以及鑒權相關的事情,通常的方案就是采用請求頭攜帶token的方式進行實現(xiàn),本文給大家介紹了Go語言中Gin框架使用JWT實現(xiàn)登錄認證的方案,需要的朋友可以參考下
    2024-11-11
  • golang使用tail實現(xiàn)追蹤文件變更

    golang使用tail實現(xiàn)追蹤文件變更

    這篇文章主要為大家介紹了golang如何借助 github.com/hpcloud/tail ,實現(xiàn)實時追蹤文件變更,達到類似shell命令tail -f的效果,感興趣的小伙伴可以了解一下
    2023-08-08
  • 詳解Golang中interface接口的原理和使用技巧

    詳解Golang中interface接口的原理和使用技巧

    interface?接口在?Go?語言里面的地位非常重要,是一個非常重要的數(shù)據(jù)結構。本文主要介紹了Golang中interface接口的原理和使用技巧,希望對大家有所幫助
    2022-11-11

最新評論

和田市| 湟源县| 萍乡市| 陈巴尔虎旗| 郓城县| 乳源| 吉安县| 陆良县| 和林格尔县| 静安区| 阿巴嘎旗| 鸡西市| 厦门市| 额敏县| 福建省| 保亭| 金山区| 顺义区| 云南省| 井冈山市| 鄢陵县| 霞浦县| 双辽市| 阆中市| 巴东县| 开化县| 东平县| 呼和浩特市| 莱西市| 福州市| 纳雍县| 济阳县| 剑阁县| 安阳市| 河北区| 古田县| 黔西县| 休宁县| 七台河市| 天等县| 温泉县|