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教程。
