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) // 安全整数