首頁 > 軟體

golang中亂數rand的使用

2022-08-17 14:01:47

1、math/rand

亂數從資源生成。包水平的函數都使用的預設的公共資源。

該資源會在程式每次執行時都產生確定的序列。如果需要每次執行產生不同的序列,應使用Seed函數進行初始化。預設資源可以安全的用於多go程並行。

關於種子seed
程式啟動的時候,種子的初始值是一樣的,也就是說亂數是一樣的,什麼意思呢?

package main

import (
   "fmt"
   "math/rand"
)

func main(){
   data := rand.Int63n(100)
   fmt.Println(data)
}
 

每次執行go run main.go
列印的都是 10

如果我們播放種子

package main

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

func main(){
   rand.Seed(time.Now().Unix()) // unix 時間戳,秒
   data := rand.Int63n(100)
   fmt.Println(data)
}

這樣每次執行go run main.go

列印的結果就不一樣,但是,根據亂數的特性,如果兩次執行的時間戳是在同一秒,那麼列印的結果是相同的。
以上的亂數相同的情況是發生在程式啟動的時候,如果程式啟動後,每次生成亂數會怎樣呢?

package main

import (
   "fmt"
   "math/rand"
)

func main(){
   for i := 0; i<5; i++ {
      data := rand.Int63n(100)
      fmt.Println(data)
   }
}

執行 go run main.go
列印
10
51
21
51
37

再次執行 go run main.go
列印
10
51
21
51
37

可見每次啟動的結果是一樣的;但是程式啟動後,每次的亂數都不盡相同,是隨機的。

如果再加上種子呢?

package main

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

func main(){
   for i := 0; i<5; i++ {
      rand.Seed(time.Now().Unix()) // unix 時間戳,秒
      data := rand.Int63n(100)
      fmt.Println(data)
   }
}

執行 go run main.go
列印
86
86
86
86
86

再次執行 go run main.go
列印
72
72
72
72
72

每次啟動程式,因為種子不一樣,所以亂數不一樣;但是程式啟動後,每次也都是播放種子,秒級時間戳,如果時間戳一樣,就導致種子一樣,生成的亂數就一樣,所以五次的亂數是一樣的。

通過上面的例子。可以知道,播放種子不是必須的,除非要求每次啟動程式的時候亂數不一樣。

並且,要設定種子的情況下,應該放在整個程式啟動的時候,而且只需要設定一次即可。修改上面的例子:

package main

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

func main(){
   rand.Seed(time.Now().UnixNano()) // 納秒時間戳
   for i := 0; i<5; i++ {
      data := rand.Int63n(100)
      fmt.Println(data)
   }
}

執行 go run main.go
列印
3
49
46
83
25

再次執行 go run main.go
列印
39
3
14
42
65

這次就是理想的結果了。使用納秒時間戳基本就沒問題了,因為我們的程式幾乎不會在1納秒時間內多次啟動的。

下面來講講rand包的具體用法

rand 包提供了兩塊的內容,一塊是基於 Rand 結構體及其方法;另一塊是基於 Rand 結構體再封裝的可直接呼叫的方法 rand.xxx,檢視原始碼就知道它們是同樣的功能。

所以,生成亂數有兩種方式

rander := rand.New(rand.NewSource(time.Now().UnixNano()))
n1 := rander.Intn(100)

rand.Seed(time.Now().UnixNano())
n2 := rand.Intn(100)

使用第一種方法,將 rander 作為包的全域性變數,這樣就只會設定一次種子。

var Rander = rand.New(rand.NewSource(time.Now().UnixNano()))

隨機整數

func (r *Rand) Int() int
func (r *Rand) Int31() int32
func (r *Rand) Int63() int64
func (r *Rand) Uint32() uint32
func (r *Rand) Uint64() uint64
func (r *Rand) Intn(n int) int
func (r *Rand) Int31n(n int32) int32
func (r *Rand) Int63n(n int64) int64

Int, Int31, Int63 生成的數都太大,一般使用 Intn, Int31n, Int63n。得到的範圍 [0, n),想要得到 [0, n],就要使用 Intn(n + 1),想要得到 [10, 100] 的亂數,就要使用 Intn(91) + 10。

隨機浮點數

func (r *Rand) Float32() float32
func (r *Rand) Float64() float64

得到 [0, 1) 之間的浮點數,32單精度,64雙精度。

基於正態分佈的隨機浮點數

func (r *Rand) NormFloat64() float64

基於指數分佈的隨機浮點數

func (r *Rand) ExpFloat64() float64

隨機序列

func (r *Rand) Perm(n int) []int

返回一個有n個元素的,[0,n)範圍內整數的偽隨機排列的切片。

Rander.Perm(10) // [1 8 0 4 7 6 3 2 9 5]

總結:

package main

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

func main() {
    s := RandString(10)
    fmt.Println(s)
}

var Rander = rand.New(rand.NewSource(time.Now().UnixNano()))

const letterString = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const numLetterString = "0123456789"

// 隨機生成字串
func RandStr(n int, letter string) string {
    str := []byte(letter)
    res := ""
    for i := 0; i < n; i++ {
        res += fmt.Sprintf("%c", str[Rander.Intn(strings.Count(letter, "") - 1)])
    }
    return res
}

func RandNumStr(n int) string {
    return RandStr(n, numLetterString)
}

func RandString(n int) string {
    return RandStr(n, letterString)
}

func RandOrder(n int) string {
    return time.Now().Format("20060102150405") + RandNumStr(n)
}

// 包含min, max
func RandNum(min , max int) int {
    return Rander.Intn(max - min + 1) + min
}

2、crypto/rand

實現了用於加解密的更安全的亂數生成器。

變數 var Reader io.Reader
是一個全域性、共用的密碼用強隨機生成器。在Unix型別系統中,會從/dev/urandom讀取;而windows中會呼叫RtlGenRandom API。

提供的方法

1、返回一個基於[0, max)的亂數

// Int returns a uniform random value in [0, max). It panics if max <= 0.
func Int(rand io.Reader, max *big.Int) (n *big.Int, err error)

範例

r, err := rand.Int(rand.Reader, big.NewInt(100))
fmt.Println(r.Int64(), err)

2、根據指定的位數bits,返回一個數,大概率是素數,(不知道這個函數是幹嘛用的)

// Prime returns a number, p, of the given size, such that p is prime
// with high probability.
// Prime will return error for any error returned by rand.Read or if bits < 2.
func Prime(rand io.Reader, bits int) (p *big.Int, err error)

範例

p, err := rand.Prime(rand.Reader, 8)
fmt.Println(p.Int64(), err)

8個二進位制位的最大值為255,此處會隨機返回小於255的素數。

3、生成隨機的二進位制序列

// Read is a helper function that calls Reader.Read using io.ReadFull.
// On return, n == len(b) if and only if err == nil.
func Read(b []byte) (n int, err error) {
    return io.ReadFull(Reader, b)
}

比如,隨機生成16個位元組的資料

var b [16]byte
n, err := rand.Read(b[:])
fmt.Println(n, err)
fmt.Println(b)

返回值
16 <nil>
[94 184 113 36 224 18 239 52 69 242 14 84 174 113 125 15]

我們可以將其轉換成16進位制數,也就是32位元

buf := make([]byte, 32)
hex.Encode(buf, b[:])
fmt.Println(string(buf))

得到
5eb87124e012ef3445f20e54ae717d0f

通過這個方法可以生成隨機的字串。

到此這篇關於golang中亂數rand的使用的文章就介紹到這了,更多相關golang亂數rand內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com