
Go 的 math/rand 包默认使用固定种子(Seed(1)),导致每次运行程序生成完全相同的“随机”序列;必须显式调用 rand.Seed()(推荐结合当前时间)才能获得真正变化的随机数。
go 的 `math/rand` 包默认使用固定种子(seed(1)),导致每次运行程序生成完全相同的“随机”序列;必须显式调用 `rand.seed()`(推荐结合当前时间)才能获得真正变化的随机数。
在 Go 中,math/rand 并非真随机,而是一个伪随机数生成器(PRNG),其输出完全由初始种子(seed)决定。若未手动设置种子,rand 会默认以 Seed(1) 初始化全局共享源(default Source),因此每次运行程序都会复现同一组数字序列——这正是你代码中始终输出 168 的根本原因(例如 rand.Intn(100) 在 seed=1 时首次返回 84,第二次也返回 84,84 + 84 = 168)。
要获得每次运行都不同的随机结果,必须在使用任何随机函数前,显式调用 rand.Seed() 并传入一个动态值。最常用且安全的方式是使用当前纳秒级时间戳:
package main
import (
"fmt"
"math/rand"
"time"
)
func add(x, y int) int {
return x + y
}
func main() {
// ✅ 关键步骤:用当前时间初始化随机源
rand.Seed(time.Now().UnixNano())
a := rand.Intn(100) // [0, 99]
b := rand.Intn(100)
fmt.Println(add(a, b))
}⚠️ 注意事项:
- time.Now().UnixNano() 比 Unix() 更优:后者仅精确到秒,在快速连续运行或高并发场景下可能重复;UnixNano() 提供纳秒级唯一性,显著降低种子冲突概率。
- *Seed() 必须在首次调用 `rand.函数前执行**:放在main()开头是最稳妥的位置;若在变量声明处(包级作用域)调用,会导致编译错误(因init阶段无法调用time.Now()`)。
-
Go 1.20+ 推荐使用 rand.New(rand.NewSource()) 方式(更现代、可并发安全):
r := rand.New(rand.NewSource(time.Now().UnixNano())) a := r.Intn(100) b := r.Intn(100)
此方式避免全局状态,适合多 goroutine 场景,也便于单元测试(可注入固定种子源)。
总结:math/rand 的确定性是设计使然,而非 bug。掌握种子初始化时机与方式,是写出可靠随机逻辑的第一步。永远记住——不 Seed,不随机。










