Go math/rand 包详解
概述
math/rand 包实现了伪随机数生成器。该包提供了生成各种类型随机数的函数,包括整数、浮点数、随机排列等。适用于模拟、测试等非安全敏感场景。
重要说明:
- ⚠️ 不应用于安全敏感场景(如密码、令牌)
- ✓ 适用于模拟、游戏、测试等场景
- ✓ 包级别函数是线程安全的
- ✓
Rand类型不是线程安全的,需要加锁 - ✓ Go 1.20+ 会自动种子化,无需手动调用 Seed
包导入
import "math/rand"
基本使用
1. 生成随机整数
package main
import (
"fmt"
"math/rand"
)
func main() {
// 生成随机整数
fmt.Printf("随机 int: %d\n", rand.Int())
fmt.Printf("随机 int64: %d\n", rand.Int63())
fmt.Printf("随机 int31: %d\n", rand.Int31())
// 生成范围内的随机数
fmt.Printf("0-99: %d\n", rand.Intn(100))
fmt.Printf("0-9: %d\n", rand.Int31n(10))
fmt.Printf("0-999: %d\n", rand.Int63n(1000))
}
2. 生成随机浮点数
package main
import (
"fmt"
"math/rand"
)
func main() {
// 生成随机浮点数
fmt.Printf("float64: %f\n", rand.Float64())
fmt.Printf("float32: %f\n", rand.Float32())
}
3. 使用 Rand 对象
package main
import (
"fmt"
"math/rand"
)
func main() {
// 创建自定义随机源
r := rand.New(rand.NewSource(42))
// 使用 Rand 对象
fmt.Printf("随机数 1: %d\n", r.Intn(100))
fmt.Printf("随机数 2: %d\n", r.Intn(100))
fmt.Printf("随机数 3: %d\n", r.Intn(100))
// 重置种子,生成相同的序列
r.Seed(42)
fmt.Printf("重置后 1: %d\n", r.Intn(100))
}
一、包级别函数
ExpFloat64
定义:
func ExpFloat64() float64
说明:
- 功能:返回指数分布的 float64
- 返回值:
float64- 范围 (0, +MaxFloat64] - 分布:速率参数 λ=1,均值=1
- 用途:模拟事件间隔时间
示例:
package main
import (
"fmt"
"math/rand"
)
func main() {
// 生成指数分布随机数
for i := 0; i < 5; i++ {
fmt.Printf("%.4f\n", rand.ExpFloat64())
}
// 调整速率参数
rate := 2.0
sample := rand.ExpFloat64() / rate
fmt.Printf("速率=%.2f 的样本:%.4f\n", rate, sample)
}
Float32
定义:
func Float32() float32
说明:
- 功能:返回 [0.0, 1.0) 范围内的随机 float32
- 返回值:
float32
示例:
package main
import (
"fmt"
"math/rand"
)
func main() {
// 生成 5 个随机 float32
for i := 0; i < 5; i++ {
fmt.Printf("%.4f\n", rand.Float32())
}
}
Float64
定义:
func Float64() float64
说明:
- 功能:返回 [0.0, 1.0) 范围内的随机 float64
- 返回值:
float64
示例:
package main
import (
"fmt"
"math/rand"
)
func main() {
// 生成 5 个随机 float64
for i := 0; i < 5; i++ {
fmt.Printf("%.6f\n", rand.Float64())
}
}
Int
定义:
func Int() int
说明:
- 功能:返回非负随机整数
- 返回值:
int- 范围 [0, MaxInt]
示例:
package main
import (
"fmt"
"math/rand"
)
func main() {
// 生成 5 个随机整数
for i := 0; i < 5; i++ {
fmt.Printf("%d\n", rand.Int())
}
}
Int31
定义:
func Int31() int32
说明:
- 功能:返回非负 31 位随机整数
- 返回值:
int32- 范围 [0, 2^31-1]
示例:
package main
import (
"fmt"
"math/rand"
)
func main() {
fmt.Printf("随机 int31: %d\n", rand.Int31())
fmt.Printf("最大值:2^31-1 = %d\n", int32(^uint32(0)>>1))
}
Int31n
定义:
func Int31n(n int32) int32
说明:
- 功能:返回 [0, n) 范围内的随机 int32
- 参数:
n- 上界(必须 > 0) - 返回值:
int32 - 注意:n <= 0 时会 panic
示例:
package main
import (
"fmt"
"math/rand"
)
func main() {
// 模拟骰子
fmt.Printf("骰子:%d\n", rand.Int31n(6)+1)
// 生成 10 个 0-99 的随机数
for i := 0; i < 10; i++ {
fmt.Printf("%d ", rand.Int31n(100))
}
}
Int63
定义:
func Int63() int64
说明:
- 功能:返回非负 63 位随机整数
- 返回值:
int64- 范围 [0, 2^63-1]
示例:
package main
import (
"fmt"
"math/rand"
)
func main() {
fmt.Printf("随机 int63: %d\n", rand.Int63())
}
Int63n
定义:
func Int63n(n int64) int64
说明:
- 功能:返回 [0, n) 范围内的随机 int64
- 参数:
n- 上界(必须 > 0) - 返回值:
int64 - 注意:n <= 0 时会 panic
示例:
package main
import (
"fmt"
"math/rand"
)
func main() {
// 大范围随机数
n := rand.Int63n(1000000)
fmt.Printf("0-999999: %d\n", n)
}
Intn
定义:
func Intn(n int) int
说明:
- 功能:返回 [0, n) 范围内的随机 int
- 参数:
n- 上界(必须 > 0) - 返回值:
int - 注意:n <= 0 时会 panic
示例:
package main
import (
"fmt"
"math/rand"
)
func main() {
// 模拟骰子
dice := rand.Intn(6) + 1
fmt.Printf("骰子点数:%d\n", dice)
// 随机数组索引
items := []string{"苹果", "香蕉", "橙子", "葡萄"}
randomItem := items[rand.Intn(len(items))]
fmt.Printf("随机选择:%s\n", randomItem)
}
NormFloat64
定义:
func NormFloat64() float64
说明:
- 功能:返回标准正态分布的 float64
- 返回值:
float64- 范围 [-MaxFloat64, +MaxFloat64] - 分布:均值=0,标准差=1
- 用途:模拟自然现象、蒙特卡洛模拟
示例:
package main
import (
"fmt"
"math"
"math/rand"
)
func main() {
// 生成标准正态分布样本
fmt.Println("标准正态分布 (μ=0, σ=1):")
for i := 0; i < 5; i++ {
fmt.Printf("%.4f\n", rand.NormFloat64())
}
// 调整均值和标准差
mean := 100.0
stddev := 15.0
sample := rand.NormFloat64()*stddev + mean
fmt.Printf("\n调整后 (μ=%.0f, σ=%.0f): %.4f\n", mean, stddev, sample)
// 验证:生成 1000 个样本计算均值
sum := 0.0
for i := 0; i < 1000; i++ {
sum += rand.NormFloat64()
}
fmt.Printf("1000 个样本的均值:%.4f (应接近 0)\n", sum/1000)
}
Perm
定义:
func Perm(n int) []int
说明:
- 功能:返回 [0, n) 的随机排列
- 参数:
n- 排列长度 - 返回值:
[]int- 随机排列的切片
示例:
package main
import (
"fmt"
"math/rand"
)
func main() {
// 生成 0-9 的随机排列
perm := rand.Perm(10)
fmt.Printf("随机排列:%v\n", perm)
// 洗牌算法
cards := []string{"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"}
indices := rand.Perm(len(cards))
fmt.Print("洗牌后:")
for _, idx := range indices {
fmt.Printf("%s ", cards[idx])
}
}
Read
定义:
func Read(p []byte) (n int, err error)
说明:
- 功能:生成随机字节填充到 p
- 参数:
p- 字节切片 - 返回值:
n- 读取的字节数(总是 len(p))err- 错误(总是 nil)
- 注意:已废弃,建议使用 crypto/rand.Read
示例:
package main
import (
"fmt"
"math/rand"
)
func main() {
// 生成随机字节
data := make([]byte, 16)
n, _ := rand.Read(data)
fmt.Printf("读取 %d 字节:%x\n", n, data)
fmt.Printf("十六进制:%X\n", data)
}
Seed
定义:
func Seed(seed int64)
说明:
- 功能:设置随机数生成器的种子
- 参数:
seed- 种子值 - 注意:
- Go 1.20+ 会自动种子化
- 相同种子产生相同的随机序列
示例:
package main
import (
"fmt"
"math/rand"
)
func main() {
// 设置种子
rand.Seed(42)
// 生成随机序列
fmt.Print("序列 1: ")
for i := 0; i < 5; i++ {
fmt.Printf("%d ", rand.Intn(100))
}
// 重置相同种子
rand.Seed(42)
fmt.Print("\n序列 2: ")
for i := 0; i < 5; i++ {
fmt.Printf("%d ", rand.Intn(100))
}
fmt.Println("\n两个序列相同!")
}
Shuffle
定义:
func Shuffle(n int, swap func(i, j int))
说明:
- 功能:随机打乱序列
- 参数:
n- 序列长度swap- 交换函数
示例:
package main
import (
"fmt"
"math/rand"
)
func main() {
// 打乱切片
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Printf("打乱前:%v\n", numbers)
rand.Shuffle(len(numbers), func(i, j int) {
numbers[i], numbers[j] = numbers[j], numbers[i]
})
fmt.Printf("打乱后:%v\n", numbers)
}
Uint32
定义:
func Uint32() uint32
说明:
- 功能:返回随机 32 位无符号整数
- 返回值:
uint32- 范围 [0, 2^32-1]
示例:
package main
import (
"fmt"
"math/rand"
)
func main() {
fmt.Printf("随机 uint32: %d\n", rand.Uint32())
fmt.Printf("十六进制:0x%08X\n", rand.Uint32())
}
二、Rand 类型
Rand 结构体
定义:
type Rand struct {
// 包含未导出的字段
}
说明:
- 功能:随机数生成器
- 特点:
- 不是线程安全的
- 可自定义 Source
- 可重现随机序列
New
定义:
func New(src Source) *Rand
说明:
- 功能:创建新的 Rand 实例
- 参数:
src- 随机源 - 返回值:
*Rand
示例:
package main
import (
"fmt"
"math/rand"
)
func main() {
// 创建自定义随机源
source := rand.NewSource(12345)
r := rand.New(source)
// 使用自定义随机源
fmt.Printf("Intn(100): %d\n", r.Intn(100))
fmt.Printf("Float64: %f\n", r.Float64())
fmt.Printf("Perm(5): %v\n", r.Perm(5))
}
NewSource
定义:
func NewSource(seed int64) Source
说明:
- 功能:创建新的随机源
- 参数:
seed- 种子值 - 返回值:
Source
示例:
package main
import (
"fmt"
"math/rand"
)
func main() {
// 创建多个独立的随机源
src1 := rand.NewSource(100)
src2 := rand.NewSource(200)
r1 := rand.New(src1)
r2 := rand.New(src2)
fmt.Printf("r1: %d, %d, %d\n", r1.Intn(1000), r1.Intn(1000), r1.Intn(1000))
fmt.Printf("r2: %d, %d, %d\n", r2.Intn(1000), r2.Intn(1000), r2.Intn(1000))
}
Rand 方法
ExpFloat64
定义:
func (r *Rand) ExpFloat64() float64
说明:
- 功能:返回指数分布的 float64
- 同包级别函数
Float32
定义:
func (r *Rand) Float32() float32
说明:
- 功能:返回 [0.0, 1.0) 范围内的随机 float32
Float64
定义:
func (r *Rand) Float64() float64
说明:
- 功能:返回 [0.0, 1.0) 范围内的随机 float64
Int
定义:
func (r *Rand) Int() int
说明:
- 功能:返回非负随机整数
Int31
定义:
func (r *Rand) Int31() int32
说明:
- 功能:返回非负 31 位随机整数
Int31n
定义:
func (r *Rand) Int31n(n int32) int32
说明:
- 功能:返回 [0, n) 范围内的随机 int32
Int63
定义:
func (r *Rand) Int63() int64
说明:
- 功能:返回非负 63 位随机整数
Int63n
定义:
func (r *Rand) Int63n(n int64) int64
说明:
- 功能:返回 [0, n) 范围内的随机 int64
Intn
定义:
func (r *Rand) Intn(n int) int
说明:
- 功能:返回 [0, n) 范围内的随机 int
NormFloat64
定义:
func (r *Rand) NormFloat64() float64
说明:
- 功能:返回标准正态分布的 float64
Perm
定义:
func (r *Rand) Perm(n int) []int
说明:
- 功能:返回 [0, n) 的随机排列
Read
定义:
func (r *Rand) Read(p []byte) (n int, err error)
说明:
- 功能:生成随机字节
Seed
定义:
func (r *Rand) Seed(seed int64)
说明:
- 功能:设置种子
Shuffle
定义:
func (r *Rand) Shuffle(n int, swap func(i, j int))
说明:
- 功能:随机打乱序列
示例:
package main
import (
"fmt"
"math/rand"
)
func main() {
r := rand.New(rand.NewSource(42))
cards := []string{"A", "K", "Q", "J", "10"}
fmt.Printf("打乱前:%v\n", cards)
r.Shuffle(len(cards), func(i, j int) {
cards[i], cards[j] = cards[j], cards[i]
})
fmt.Printf("打乱后:%v\n", cards)
}
Uint32
定义:
func (r *Rand) Uint32() uint32
说明:
- 功能:返回随机 32 位无符号整数
三、Source 类型
Source 接口
定义:
type Source interface {
Int63() int64
Seed(seed int64)
}
说明:
- 功能:随机源接口
- 用途:实现自定义随机数生成器
四、典型示例
示例 1:生成随机密码
package main
import (
"fmt"
"math/rand"
"time"
)
func generatePassword(length int) string {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
// Go 1.20+ 不需要手动 Seed
// rand.Seed(time.Now().UnixNano())
password := make([]byte, length)
for i := 0; i < length; i++ {
password[i] = charset[rand.Intn(len(charset))]
}
return string(password)
}
func main() {
fmt.Println("随机密码生成:")
for i := 0; i < 5; i++ {
fmt.Printf("%d: %s\n", i+1, generatePassword(12))
}
}
示例 2:随机抽样
package main
import (
"fmt"
"math/rand"
)
// 从总体中随机抽取 k 个样本(不放回)
func sample(population []string, k int) []string {
if k > len(population) {
k = len(population)
}
// 复制总体
pool := make([]string, len(population))
copy(pool, population)
// Fisher-Yates 洗牌
for i := 0; i < k; i++ {
j := i + rand.Intn(len(pool)-i)
pool[i], pool[j] = pool[j], pool[i]
}
return pool[:k]
}
func main() {
population := []string{"Alice", "Bob", "Charlie", "David", "Eve",
"Frank", "Grace", "Henry", "Ivy", "Jack"}
fmt.Println("从 10 人中随机抽取 3 人:")
for i := 0; i < 3; i++ {
fmt.Printf("第 %d 次:%v\n", i+1, sample(population, 3))
}
}
示例 3:蒙特卡洛模拟(计算π)
package main
import (
"fmt"
"math/rand"
)
func estimatePi(samples int) float64 {
inside := 0
for i := 0; i < samples; i++ {
x := rand.Float64()
y := rand.Float64()
// 检查点是否在单位圆内
if x*x + y*y <= 1 {
inside++
}
}
// π/4 ≈ inside/samples
return 4.0 * float64(inside) / float64(samples)
}
func main() {
fmt.Println("蒙特卡洛方法估算π:")
samples := []int{1000, 10000, 100000, 1000000}
for _, n := range samples {
pi := estimatePi(n)
fmt.Printf("样本数 %7d: π ≈ %.6f\n", n, pi)
}
}
示例 4:随机漫步模拟
package main
import (
"fmt"
"math/rand"
)
func randomWalk(steps int) []int {
position := 0
path := []int{0}
for i := 0; i < steps; i++ {
if rand.Intn(2) == 0 {
position++
} else {
position--
}
path = append(path, position)
}
return path
}
func main() {
fmt.Println("随机漫步模拟:")
path := randomWalk(20)
for i, pos := range path {
fmt.Printf("步骤 %2d: %3d ", i, pos)
// 可视化
if pos >= 0 {
fmt.Print("→ ")
for j := 0; j < pos; j++ {
fmt.Print(" ")
}
fmt.Println("●")
} else {
fmt.Print("← ")
for j := 0; j < -pos; j++ {
fmt.Print(" ")
}
fmt.Println("●")
}
}
fmt.Printf("\n最终位置:%d\n", path[len(path)-1])
}
示例 5:模拟掷骰子
package main
import (
"fmt"
"math/rand"
)
func rollDice(sides int) int {
return rand.Intn(sides) + 1
}
func rollMultipleDice(count, sides int) []int {
results := make([]int, count)
for i := 0; i < count; i++ {
results[i] = rollDice(sides)
}
return results
}
func main() {
fmt.Println("掷骰子模拟:")
// 掷 10 次 6 面骰
fmt.Println("\n10 次 d6:")
for i := 0; i < 10; i++ {
fmt.Printf("%d ", rollDice(6))
}
// 掷 5 次 20 面骰
fmt.Println("\n\n5 次 d20:")
for i := 0; i < 5; i++ {
fmt.Printf("%d ", rollDice(20))
}
// 3 个 6 面骰
fmt.Println("\n\n3d6 (5 次):")
for i := 0; i < 5; i++ {
rolls := rollMultipleDice(3, 6)
sum := 0
for _, r := range rolls {
sum += r
}
fmt.Printf("%v = %d\n", rolls, sum)
}
}
示例 6:随机字符串生成
package main
import (
"fmt"
"math/rand"
)
const (
letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
digits = "0123456789"
specialChars = "!@#$%^&*()_+-=[]{}|;:,.<>?"
)
func randomString(length int, charset string) string {
b := make([]byte, length)
for i := range b {
b[i] = charset[rand.Intn(len(charset))]
}
return string(b)
}
func main() {
fmt.Println("随机字符串生成:")
// 纯字母
fmt.Printf("字母 (10): %s\n", randomString(10, letters))
// 字母 + 数字
fmt.Printf("字母数字 (10): %s\n", randomString(10, letters+digits))
// 所有字符
fmt.Printf("所有字符 (10): %s\n", randomString(10, letters+digits+specialChars))
}
示例 7:抽奖系统
package main
import (
"fmt"
"math/rand"
)
type Lottery struct {
participants []string
winners []string
}
func NewLottery() *Lottery {
return &Lottery{
participants: make([]string, 0),
winners: make([]string, 0),
}
}
func (l *Lottery) AddParticipant(name string) {
l.participants = append(l.participants, name)
}
func (l *Lottery) DrawWinners(count int) []string {
if count > len(l.participants) {
count = len(l.participants)
}
// 复制参与者列表
pool := make([]string, len(l.participants))
copy(pool, l.participants)
// 抽取获奖者
winners := make([]string, 0, count)
for i := 0; i < count; i++ {
idx := rand.Intn(len(pool))
winners = append(winners, pool[idx])
// 移除已中奖者
pool[idx] = pool[len(pool)-1]
pool = pool[:len(pool)-1]
}
l.winners = append(l.winners, winners...)
return winners
}
func main() {
lottery := NewLottery()
// 添加参与者
participants := []string{"张三", "李四", "王五", "赵六", "钱七",
"孙八", "周九", "吴十", "郑十一", "王十二"}
for _, p := range participants {
lottery.AddParticipant(p)
}
fmt.Println("抽奖系统")
fmt.Printf("参与者:%d 人\n", len(participants))
// 抽取 3 名获奖者
fmt.Println("\n开始抽奖...")
winners := lottery.DrawWinners(3)
fmt.Println("\n获奖者名单:")
for i, w := range winners {
fmt.Printf("%d. %s\n", i+1, w)
}
}
示例 8:并发安全的随机数生成
package main
import (
"fmt"
"math/rand"
"sync"
)
// 线程安全的随机数生成器
type SafeRand struct {
mu sync.Mutex
r *rand.Rand
}
func NewSafeRand(seed int64) *SafeRand {
return &SafeRand{
r: rand.New(rand.NewSource(seed)),
}
}
func (sr *SafeRand) Intn(n int) int {
sr.mu.Lock()
defer sr.mu.Unlock()
return sr.r.Intn(n)
}
func (sr *SafeRand) Float64() float64 {
sr.mu.Lock()
defer sr.mu.Unlock()
return sr.r.Float64()
}
func main() {
safeRand := NewSafeRand(42)
var wg sync.WaitGroup
results := make([]int, 10)
// 并发生成随机数
for i := 0; i < 10; i++ {
wg.Add(1)
go func(idx int) {
defer wg.Done()
results[idx] = safeRand.Intn(100)
}(i)
}
wg.Wait()
fmt.Println("并发安全的随机数:")
fmt.Println(results)
}
五、最佳实践
1. 使用包级别函数(简单场景)
// ✓ 好的做法:简单场景使用包级别函数
n := rand.Intn(100)
f := rand.Float64()
// 包级别函数是线程安全的
2. 使用 Rand 对象(需要控制种子)
// ✓ 好的做法:需要重现性时使用 Rand
r := rand.New(rand.NewSource(42))
n1 := r.Intn(100)
r.Seed(42)
n2 := r.Intn(100) // n1 == n2
3. Go 1.20+ 不需要手动 Seed
// Go 1.20+ 会自动种子化
// 不需要:rand.Seed(time.Now().UnixNano())
// ✓ 直接使用
n := rand.Intn(100)
4. 并发场景使用锁
// ✓ 好的做法:并发时使用锁
type SafeRand struct {
mu sync.Mutex
r *rand.Rand
}
5. 安全敏感场景使用 crypto/rand
// ✗ 错误:不应用于安全场景
password := rand.Intn(1000000) // 不安全!
// ✓ 好的做法:使用 crypto/rand
import "crypto/rand"
六、与其他包配合
1. 与 time 包配合(Go 1.19 及以下)
import (
"math/rand"
"time"
)
// Go 1.19 及以下需要手动种子化
rand.Seed(time.Now().UnixNano())
2. 与 crypto/rand 配合
import (
"crypto/rand"
"math/big"
)
// 生成加密安全的随机数
n, _ := rand.Int(rand.Reader, big.NewInt(100))
3. 与 slices 包配合
import (
"math/rand"
"slices"
)
// 打乱切片
slices.Shuffle(data, func(i, j int) {
data[i], data[j] = data[j], data[i]
})
七、快速参考
包级别函数
| 函数 | 参数 | 返回值 | 功能 |
|---|---|---|---|
ExpFloat64 | 无 | float64 | 指数分布 |
Float32 | 无 | float32 | [0,1) 随机 float32 |
Float64 | 无 | float64 | [0,1) 随机 float64 |
Int | 无 | int | 随机非负 int |
Int31 | 无 | int32 | 随机 31 位 int |
Int31n | n int32 | int32 | [0,n) 随机 int32 |
Int63 | 无 | int64 | 随机 63 位 int |
Int63n | n int64 | int64 | [0,n) 随机 int64 |
Intn | n int | int | [0,n) 随机 int |
NormFloat64 | 无 | float64 | 标准正态分布 |
Perm | n int | []int | [0,n) 随机排列 |
Read | p []byte | (int, error) | 随机字节 |
Seed | seed int64 | 无 | 设置种子 |
Shuffle | n int, swap | 无 | 打乱序列 |
Uint32 | 无 | uint32 | 随机 uint32 |
Rand 方法
Rand 类型的方法与包级别函数基本相同,只是需要通过 Rand 实例调用。
八、注意事项
1. 不适用于安全场景
// ✗ 不安全的做法
token := rand.Int63() // 可预测!
// ✓ 安全的做法
import "crypto/rand"
n, _ := rand.Int(rand.Reader, big.NewInt(1000000))
2. 并发安全
// 包级别函数是线程安全的
// Rand 对象不是线程安全的,需要加锁
3. 种子重复
// 相同种子产生相同序列
rand.Seed(42)
fmt.Println(rand.Intn(100)) // 总是相同
// Go 1.20+ 自动种子化,避免此问题
4. 范围验证
// n <= 0 会 panic
rand.Intn(0) // panic!
rand.Intn(-1) // panic!
// ✓ 好的做法
if n > 0 {
rand.Intn(n)
}
最后更新: 2026-04-05
Go 版本: Go 1.20+
包文档: https://pkg.go.dev/math/rand