Golang中生成随机数

Gorandom教程展示了如何在Golang中生成随机值。

$ go version
go version go1.18.1 linux/amd64

我们使用Go版本1.18。

随机数生成器

随机数生成器(RNG)生成一组值,这些值在外观上不显示任何可区分的模式。随机数发生器分为两类:硬件随机数发生器和伪随机数发生器。硬件随机数生成器被认为可以产生真正的随机数。伪随机数生成器根据软件算法生成值。它们产生看起来随机的值。但是这些值是确定性的并且可以重现,如果算法是已知的。

在计算中,随机生成器用于赌博、游戏、模拟或密码学。

为了提高伪随机数生成器的质量,操作系统使用从设备驱动程序、用户输入延迟或一个或多个硬件组件的抖动收集的环境噪声。这是加密安全伪随机数生成器的核心。

Go包含实现伪随机数生成器的math/random包,以及实现加密安全随机数生成器的crypto/rand包。

种子

种子是初始化随机数生成器的值。随机数生成器通过对先前值执行某些操作来生成值。当算法开始时,种子是生成器运行的初始值。生成器最重要和最困难的部分是提供接近真正随机数的种子。

在Go中,种子值由rand.Seed函数提供。如果未调用Seed,则生成器的行为就像通过Seed(1)播种一样。

随机选择相同的种子

在下面的例子中,我们使用相同的种子。

package main

import (
    "fmt"
    "math/rand"
)

func main() {

    rand.Seed(20)
    fmt.Printf("%d ", rand.Intn(100))
    fmt.Printf("%d ", rand.Intn(100))
    fmt.Printf("%d \n", rand.Intn(100))

    rand.Seed(20)
    fmt.Printf("%d ", rand.Intn(100))
    fmt.Printf("%d ", rand.Intn(100))
    fmt.Printf("%d \n", rand.Intn(100))

    fmt.Println()
}

相同的种子值产生相同的伪随机值。

$ go run same_seed.go 
30 48 40 
30 48 40 

去rand.Intn

rand.Intn函数以int形式从默认源返回[0,n)中的非负伪随机数。

package main

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

func init() {

    rand.Seed(time.Now().UnixNano())
}

func main() {

    for i := 0; i < 5; i++ {

        fmt.Printf("%d ", rand.Intn(20))
    }

    fmt.Println()
}

该示例打印五个随机整数。

rand.Seed(time.Now().UnixNano())

为了获得不同的伪随机值,我们使用time.Now().UnixNano()为随机生成器播种。

随机字符串

以下示例生成随机字符串。

package main

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

func main() {

    rand.Seed(time.Now().UTC().UnixNano())
    fmt.Println(randomString(12))
}

func randomString(len int) string {

    bytes := make([]byte, len)

    for i := 0; i < len; i++ {
        bytes[i] = byte(randInt(97, 122))
    }

    return string(bytes)
}

func randInt(min int, max int) int {

    return min + rand.Intn(max-min)
}

该示例创建了一个包含十二个字符的随机字符串。

$ go run random_string.go 
gqvqyybfuhxl
$ go run random_string.go 
rrwmqaqkrslu
$ go run random_string.go 
axhhrkwyhnxm

我们运行示例3次。

随机整数数组

以下示例创建一个随机整数值数组。

package main

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

func randArray(len int) []int {

    a := make([]int, len)

    for i := 0; i <= len-1; i++ {

        a[i] = rand.Intn(len)
    }

    return a
}

func main() {

    rand.Seed(time.Now().UnixNano())

    len := 12
    fmt.Println(randArray(len))
}

该示例创建了一个包含十二个整数值的数组。

$ go run rand_array.go 
[5 6 3 5 3 4 7 3 5 6 5 0]
$ go run rand_array.go 
[2 5 10 9 1 5 1 4 11 7 6 3]

去随机元素

以下示例选择一个随机元素。

package main

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

func init() {

    rand.Seed(time.Now().UnixNano())
}

func main() {

    runes := []rune("červená čiara")

    myrune := runes[rand.Intn(len(runes))]

    fmt.Println(string(myrune))
}

我们有一片符文。从这个切片中,我们随机选择一个值。

$ go run random_element.go 
č
$ go run random_element.go 
á

我们运行了两次示例并获得了这些字符。

从池中随机生成字符串

以下示例从字符池中随机选取字母。

package main

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

var pool = "abcdefghijklmnopqrstuvwxyzABCEFGHIJKLMNOPQRSTUVWXYZ:|?$%@][{}#&/()*"

func randomString(l int) string {

    bytes := make([]byte, l)

    for i := 0; i < l; i++ {
        bytes[i] = pool[rand.Intn(len(pool))]
    }

    return string(bytes)
}

func main() {

    rand.Seed(time.Now().UnixNano())
    fmt.Println(randomString(12))
}

该示例从各种字符的预定义池中打印随机字符串。

var pool = "abcdefghijklmnopqrstuvwxyzABCEFGHIJKLMNOPQRSTUVWXYZ:|?$%@][{}#&/()*"

这是一组预定义的字符。

for i := 0; i < l; i++ {
    bytes[i] = pool[rand.Intn(len(pool))]
}

我们通过生成字符串的随机索引来选择一个随机字母。

$ go run rand_pool.go 
FFFZW(sHvE:a
$ go run rand_pool.go 
(my%Tmf&qOVs
$ go run rand_pool.go 
/{GqgkhRVOfi

我们运行示例3次。

Go加密安全随机值

Go在标准库包crypto/rand中提供了加密安全的伪随机数生成器。虽然math/random快得多,但crypto/rand适用于安全性至关重要的程序。例如,在生成强密码、CSRF令牌或会话密钥时。

在Linux和FreeBSD上,crypto/rand如果可用则使用getrandom,否则使用/dev/urandom。在OpenBSD上,它使用getentropy。类Unix系统,它从/dev/urandom读取。在Windows系统上,它使用CryptGenRandom函数。在Wasm上,它使用WebCryptoAPI。

package main

import (
    "crypto/rand"
    "fmt"
    "log"
)

func main() {

    data, err := generateRandomBytes(16)

    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(data)
}

func generateRandomBytes(n int) ([]byte, error) {

    b := make([]byte, n)
    _, err := rand.Read(b)
    
    if err != nil {
        return nil, err
    }

    return b, nil
}

在代码示例中,我们创建了16个安全生成的随机字节。

b := make([]byte, n)
_, err := rand.Read(b)

我们读取n个加密安全伪随机数并将它们写入字节片。

$ go run crypto_rand.go 
[151 0 67 88 199 60 220 50 34 198 169 158 18 162 85 61]

在本教程中,我们使用了Golang中的随机值。

列出所有Go教程。

赞(0) 打赏

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏