因为没初始化随机种子,Go的rand.Intn默认使用固定种子0(或1),导致每次运行生成相同序列;正确做法是Go 1.20+推荐用rand.New(rand.NewSource(time.Now().UnixNano()))创建独立实例。

Go 的 rand.Intn 为什么每次运行都返回相同数字?
因为没初始化随机种子。Go 的 rand 包默认使用固定种子(0),所以 rand.Intn(10) 每次运行都输出一模一样的序列。
正确做法是调用 rand.Seed(),但注意:Go 1.20+ 已弃用该函数,推荐直接用 rand.New(rand.NewSource(time.Now().UnixNano())) 创建独立的 *rand.Rand 实例。
- 别再用
rand.Seed(time.Now().UnixNano())全局设置——它只影响rand包的全局实例,且在多 goroutine 下不安全 - 如果你只是临时取一个随机数,用
rand.New(rand.NewSource(time.Now().UnixNano())).Intn(100)更可靠 - 若需多次调用,把生成的
*rand.Rand实例缓存起来复用,避免重复创建开销
如何生成 [0, 1) 区间的浮点随机数?
rand.Float64() 返回的就是这个范围,但它也依赖全局实例,同样存在种子未初始化或并发问题。
更稳妥的写法是:
立即学习“go语言免费学习笔记(深入)”;
src := rand.NewSource(time.Now().UnixNano()) r := rand.New(src) f := r.Float64() // [0.0, 1.0)
-
rand.Float32()和rand.Float64()都不能直接指定范围,要缩放得自己乘系数,比如r.Float64() * 5.0得到 [0, 5) - 不要用
int(r.Float64() * 10)去替代Intn(10)——浮点转整可能因精度问题越界(极小概率) - 如果需要高精度均匀分布的浮点数,确保
src是加密安全的(如用crypto/rand),但普通模拟场景没必要
模拟场景下要不要用 crypto/rand?
不需要。除非你在生成密钥、token 或做安全敏感的抽样,否则 crypto/rand 是过度设计,而且慢、难调试、不支持自定义种子(无法复现结果)。
-
crypto/rand不提供Intn这类便捷方法,得自己读字节再裁剪,容易出错 - 模拟测试、游戏逻辑、数据生成等场景,用
math/rand+ 显式rand.NewSource(seed)即可;固定 seed 还能保证结果可重现 - 想快速验证逻辑?传入固定 seed,比如
rand.New(rand.NewSource(42)),比反复改代码看输出高效得多
常见错误:rand.Int() 返回负数?
是的,rand.Int() 返回的是 int64 范围内的任意整数(含负),不是“正随机数”。它几乎没人用,容易误解。
- 真正常用的是
Intn(n)([0,n))、Int31n(n)(int32 版本)、Uint32()等明确语义的函数 -
Int()的源码就是直接读取src.Int63(),然后强制类型转换,没有任何范围控制 - 看到有人用
rand.Int() % 100,这不仅符号不可控,还引入了模偏差(小数值概率略高),应直接用Intn(100)
模拟时最常被忽略的其实是种子管理方式——用全局实例看似简单,但一旦并发或单元测试并行执行,就可能相互干扰;而每次都 new 一个带 time.Now() 的实例,又会让测试无法复现。真正稳的做法,是在模拟入口处接受 seed 参数,测试时传固定值,生产时传 time.Now().UnixNano()。


















