Go 中 math/rand 生成随机数
FreeGuideOnline
最新
2026-07-07
go import ( "fmt" "math/rand" "time" )
---
### 设置随机种子
计算机生成的随机数其实是“伪随机数”,它们是按固定算法计算出来的序列。如果不设置种子,每次运行程序都会得到完全相同的序列。为了让每次运行产生不同的随机数,必须设置一个变化的**种子值**。
最常用的做法是把当前时间转换为纳秒级时间戳作为种子:
```go
rand.Seed(time.Now().UnixNano())
注意:从 Go 1.20 开始,
rand.Seed已被弃用,全局随机源会自动用随机值初始化,无需手动调用。但掌握种子的概念对理解旧代码仍有帮助。本教程中的代码兼容 Go 1.20 之前的版本,新版本中可省略Seed。
生成随机整数
生成无范围限制的整数
rand.Int():返回一个非负的int类型随机数(范围极大)。rand.Int31():返回一个非负的int32随机数。rand.Int63():返回一个非负的int64随机数。
n := rand.Int() // 例如:5577006791947779410
m := rand.Int31() // 例如:1303797291
k := rand.Int63() // 例如:2429320772257494948
生成指定范围内的整数(左闭右开)
rand.Intn(n) 返回一个 [0, n) 之间的整数,即包含 0,不包含 n。
// 生成 0~99 的随机整数
num := rand.Intn(100)
如果要得到 [min, max) 区间的数,可以这样写:
min := 10
max := 20
// 生成 10~19 的随机整数
num := rand.Intn(max-min) + min
同样,还有 rand.Int31n(n) 返回 [0, n) 的 int32 类型。
生成 0 到 1 之间的负整数?
通常不需要,但你可以用 -rand.Intn(n+1) 来生成负随机数。
生成随机浮点数
rand.Float32():返回[0.0, 1.0)之间的float32。rand.Float64():返回[0.0, 1.0)之间的float64。
f32 := rand.Float32() // 例如:0.337562
f64 := rand.Float64() // 例如:0.059678
生成指定范围的浮点数 [min, max):
min := 1.5
max := 5.5
result := min + rand.Float64()*(max-min)
生成其他类型的随机数
随机布尔值
没有直接方法,可以借助整数或 Intn(2) 实现:
b := rand.Intn(2) == 1 // 50% 概率为 true
随机字节序列
使用 rand.Read(p []byte) 向字节切片中填充随机字节:
buf := make([]byte, 8)
rand.Read(buf)
fmt.Printf("%x\n", buf) // 输出 16 进制随机字节
从切片/数组中随机选取元素
写一个辅助函数即可轻松实现:
func randomPick[T any](slice []T) T {
return slice[rand.Intn(len(slice))]
}
colors := []string{"红色", "蓝色", "绿色", "黄色"}
c := randomPick(colors) // 随机得到一个颜色
对于 Go 1.18+ 的泛型版本,可适用于任何切片类型。
打乱切片顺序(洗牌)
rand.Shuffle 可以原地随机打乱切片中元素的顺序:
numbers := []int{1, 2, 3, 4, 5, 6}
rand.Shuffle(len(numbers), func(i, j int) {
numbers[i], numbers[j] = numbers[j], numbers[i]
})
// 示例结果:[3, 5, 2, 1, 6, 4]
生成指定长度的随机字符串
结合 rand.Intn 和字符集,可以随机组成字符串:
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
func randomString(length int) string {
b := make([]byte, length)
for i := range b {
b[i] = charset[rand.Intn(len(charset))]
}
return string(b)
}
fmt.Println(randomString(10)) // 例如:aZ9xK7bQm2
使用独立的随机源(避免全局锁竞争)
在高并发场景下,全局的 rand 函数内部有锁保护,可能成为性能瓶颈。此时可以创建独立的 *rand.Rand 实例:
// 创建基于特定种子的本地随机源
src := rand.NewSource(time.Now().UnixNano())
rng := rand.New(src) // rng 是 *rand.Rand 类型
// 使用方法和全局函数一致,但无需锁竞争
n := rng.Intn(100)
f := rng.Float64()
从 Go 1.20 开始,rand.New 也可以使用自动种子,但需要显式调用 rand.New(rand.NewSource(time.Now().UnixNano()))。
加密安全的随机数
math/rand 用于模拟、游戏等非安全场景,绝不用于密码学用途。需要加密安全随机数时,请使用 crypto/rand 包:
import (
"crypto/rand"
"math/big"
)
// 生成加密安全的 [0, max) 随机整数
n, _ := rand.Int(rand.Reader, big.NewInt(100))
fmt.Println(n) // 安全整数