因为math/rand默认种子固定为1,未初始化种子时每次运行都从同一状态生成确定性序列;正确做法是用rand.new(rand.newsource(time.now().unixnano()))创建独立实例。

Go 语言默认的 rand.Intn 不是真随机,不加种子会反复生成相同序列——必须调用 rand.Seed 或改用 rand.New(rand.NewSource(time.Now().UnixNano()))。
为什么每次运行 rand.Intn(10) 都返回同一个数?
因为 Go 的 math/rand 包使用确定性伪随机算法,初始种子固定为 1。没手动设置种子时,每次程序启动都从同一状态开始。
- 错误写法:
rand.Intn(10)(未初始化种子,或只在包初始化里调一次rand.Seed) - 正确做法:在 main 入口或首次使用前,用纳秒级时间设种:
rand.Seed(time.Now().UnixNano()) - 更推荐方式:创建独立的
*rand.Rand实例,避免全局状态干扰:r := rand.New(rand.NewSource(time.Now().UnixNano())),再调r.Intn(10) - 注意:
time.Now().Unix()秒级精度不够,高并发下可能重复;务必用UnixNano()
rand.Intn 和 rand.Int63n 有什么区别?
两者都生成 [0, n) 区间整数,但底层实现和适用范围不同:
-
rand.Intn(n)要求n ,内部调用 <code>Int63n,适合一般场景(如取数组索引、骰子点数) -
rand.Int63n(n)更底层,仅支持n ,但比 <code>Intn少一层封装,性能略高;若n > 1,得用 <code>Int64()手动取模(注意模偏差) - 别用
rand.Int() % n:Int()返回 int(32/64 位依赖系统),且取模会导致分布不均,尤其当n接近1 时
需要加密安全随机数时,不能用 math/rand
比如生成 token、密钥、验证码盐值——math/rand 是伪随机,可被预测;必须换 crypto/rand。
-
crypto/rand读取操作系统熵池(Linux 的/dev/urandom,Windows 的 BCryptGenRandom),不可预测 - 生成随机字节:
crypto/rand.Read(buf),失败需检查 err - 生成 0~n-1 整数:没有直接等价函数,需先读足够字节再裁剪+拒绝采样(
crypto/rand自带Int函数,接受*big.Int上界) - 示例:
n := big.NewInt(100); r, _ := rand.Int(rand.Reader, n); fmt.Println(r.Int64())
真正难的不是“怎么生成”,而是判断该用 math/rand 还是 crypto/rand——业务逻辑里混用或误判安全性要求,比语法错误更隐蔽也更危险。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











