Go语言生成随机数

在日常开发中,生成随机数是很常见的需求,Go 语言有两种方式来生成随机数,分别由 math/rand 和 crypo/rand 库来提供。

为什么会提供两种方式呢?其实两者是有区别的,math 提供的是伪随机数,生成的随机序列不是真正的随机;而 crypo 提供的随机数具有更好的随机性,可以满足密码对随机数的要求,但缺点是性能较差,据资料显示相差 10 倍左右。

(一)math/rand

伪随机数生成的随机数是确定的。相同的程序不管什么时间、在什么机器上执行,生成的随机数序列都是相同的。

1
2
3
4
5
6
7
8
9
func main() {
	for i:=0; i<10; i++ {
        fmt.Printf("%d ", rand.Intn(10))
	}
}
$ go run main.go
1 7 7 9 1 8 5 0 6 0
$ go run main.go
1 7 7 9 1 8 5 0 6 0

可以看到,程序执行多次产生的随机数是一样的,不够随机。我们可以通过设置随机种子,也可以理解为随机函数增加参数。

1
2
3
4
5
6
7
8
9
10
func main() {
	rand.Seed(1009)
	for i:=0; i<10; i++ {
        fmt.Printf("%d ", rand.Intn(10))
	}
}
$ go run main.go
9 4 0 6 5 6 1 5 4 9
$ go run main.go
9 4 0 6 5 6 1 5 4 9

在生成随机数之前,调用 Seed 函数设置随机数种子,可以发现生成的随机数终于发生了变化。但重新执行程序时,生成的随机序列仍然是一样的。

这里有个问题,只要 Seed 种子一样,每次程序启动都是按照一样的随机数去生成。为了更加随机,我们可以把种子设置为时间戳。

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
	rand.Seed(time.Now().UnixNano())
	for i:=0; i<10; i++ {
        fmt.Printf("%d ", rand.Intn(10))
	}
}

$ go run main.go
2 4 2 8 5 7 0 0 4 0
$ go run main.go 
2 1 9 2 7 3 0 1 4 8
$ go run main.go
0 3 8 3 9 2 7 3 5 6

可以看到,基本上能够实现了随机数的生成,可以满足日常大多数工作需求。

(二)crypto/rand

crypto 提供真正的随机数,能够实现更好的随机性,可以满足密码对随机数的要求,但缺点就是性能比较慢

1
2
3
4
5
6
func main() {
   for i := 0; i < 10; i++  {
      n, _ := rand.Int(rand.Reader, big.NewInt(100))
      println(n.Int64())
   }
}

如上述代码,使用非常简单。据相关测试实验表明,跟 math 提供随机数性能相比,要差 10 倍左右。

两者各有优缺点,需要根据实际需求来选择即可。对于涉及密码类的开发工作要选用 crypto/rand,对于不涉及密码类开发的工作,可以选择 math/rand 一般就能满足常规随机数的需求。

你可能感兴趣的