如何使用Golang的math/rand包生成随机数_Golang随机数生成与控制方法

4次阅读

math/rand默认种子为1导致每次运行随机数相同,需用time.Now().unixNano()设种;生成[1,100]整数应写为1+r.Intn(100);并发下应避免全局函数,推荐局部*rand.Rand实例或sync.Pool复用。

如何使用Golang的math/rand包生成随机数_Golang随机数生成与控制方法

为什么 math/rand 生成的随机数每次运行都一样?

因为默认种子(seed)固定为 1,rand.NewSource(1) 导致序列完全可复现。这不是 bug,是设计使然——方便测试,但生产环境必须手动设种。

正确做法是用当前时间纳秒级精度初始化:

src := rand.NewSource(time.Now().UnixNano()) r := rand.New(src)

注意别在循环里反复调用 time.Now() 初始化,否则高并发下可能撞上相同种子;全局复用一个 *rand.Rand 实例更安全。

如何生成指定范围的随机整数(比如 [1, 100])?

rand.Intn(n) 返回 [0, n) 的整数,不能直接套用。要得到闭区间 [min, max],得做偏移和长度校准:

立即学习go语言免费学习笔记(深入)”;

  • 先算长度:max - min + 1
  • 再加偏移:min + r.Intn(max-min+1)

例如生成 1 到 100(含):

num := 1 + r.Intn(100) // ✅ 不是 r.Intn(100) + 1 写法错误,但语义等价;重点是范围计算别漏 +1

常见错误:写成 r.Intn(100) + 1 看似对,但如果 min 是变量,漏掉 +1 就变成 [min, max) 了。

如何生成随机浮点数或字符串

math/rand 提供 float32()Float64(),返回 [0.0, 1.0) 区间值,缩放即可:

// [5.0, 15.0) f := 5.0 + r.Float64()*10.0

生成随机字符串需组合使用 r.Intn() 和字符集:

const letters = "abcdefghijklmnopqrstuvwxyz0123456789" b := make([]byte, 8) for i := range b {     b[i] = letters[r.Intn(len(letters))] } s := string(b)

注意:不要用 rand.Read() 混淆——那是 crypto/rand 的函数,用于密码学安全场景,性能差、阻塞式,math/rand 不提供该方法。

并发环境下直接用全局 rand.* 函数安全吗?

不安全。rand.Intn() 等全局函数操作的是包级全局变量 rand.Rand,内部用 mutex 保护,但会成为性能瓶颈,且 go 1.20+ 已标记为「不推荐」。

推荐方式是每个 goroutine 自持实例,或用 sync.Pool 复用:

var randPool = sync.Pool{     New: func() interface{} {         return rand.New(rand.NewSource(time.Now().UnixNano()))     }, }

然后在 goroutine 中取用:r := randPool.Get().(*rand.Rand),用完记得 randPool.Put(r)。不过多数服务中,直接用带 seed 的局部 *rand.Rand 更清晰可控。

真正容易被忽略的是:哪怕你用了 sync.Pool,也别把 time.Now().UnixNano() 放在 New 函数里——池中对象可能复用,重复设种反而导致随机性下降。应该在每次 Get 后重置 seed,或干脆只用一次性的实例。

text=ZqhQzanResources