跳到主要内容

go 语言实践:随机数生成

· 阅读需 4 分钟
一介布衣
全栈开发者 / 技术写作者

  • 文章目录

    • go语言中的随机数
      • 当种子值相同时
      • 当用当前时间作为随机种子的时候
      • 如果每次生成随机数都用当前时间作为随机种子应该就没问题吧?
  • go语言中的随机数

math/rand包操作随机数,
rand.Seed(seedNum)方法设定种子值,
rand.Intn(n)获取一个小于n的随机数,[ 同一个种子值,生成的随机数是确定的 ]
所以go语言中的随机数取决于种子的随机性,
一般情况下可以采用系统时间的毫秒数作为种子值.

当种子值相同时

package main

import (
"fmt"
"math/rand"
)

func main() {
rand.Seed(888)
//获取8个小于1000的随机数
for i := 0; i < 8; i++ {
fmt.Printf("%d ", rand.Intn(1000))
}
fmt.Println("--------")

//生成随机数
rand.Seed(888)
for i := 0; i < 8; i++ {
fmt.Printf("%d ", rand.Intn(1000))
}
fmt.Println("--------")
}

结果随机数也相同:

294   303   749   471   664   832   888   221   --------
294 303 749 471 664 832 888 221 --------

当用当前时间作为随机种子的时候

package main

import (
"fmt"
"math/rand"
"time"
)

func main() {
var seedNum int64

//用系统时间作为随机数种子
seedNum = time.Now().UnixNano()

rand.Seed(seedNum)
for i := 0; i < 8; i++ {
fmt.Printf("%d ", rand.Intn(1000))
}
fmt.Println("--------")

//系统时间延迟1秒钟,以获得新的时间
time.Sleep(time.Second)
seedNum = time.Now().UnixNano()
//延迟1秒后生成随机数
rand.Seed(seedNum)
for i := 0; i < 8; i++ {
fmt.Printf("%d ", rand.Intn(1000))
}
fmt.Println("--------")
}

先用系统当前时间作为随机种子,
然后延迟1秒,再获取当前时间作为随机种子
结果如下:

0   528   627   162   63   489   324   27   --------
623 454 974 844 923 684 460 184 --------

如果每次生成随机数都用当前时间作为随机种子应该就没问题吧?

我们测试一下这种情况,比如连续生成8个随机验证码

package main

import (
"fmt"
"math/rand"
"time"
)

func main() {

for i := 0; i < 10; i++ {
//当前时间戳设置为随机种子
rand.Seed(time.Now().Unix())
//存放验证码的数组切片
bytes := make([]byte, 8)
for i := 0; i < 8; i++ {
//c的ascii码值:65~90为26个大写英文字母,97~122号为26个小写英文字母
//其余为一些标点符号、运算符号等
//我们要获取8个大写字母的验证码
b := rand.Intn(26) + 65
bytes[i] = byte(b)
}
fmt.Println(string(bytes))
}

}

上面 rand.Seed 设置随机种子的时候是在 for 循环内部;
每次循环都获取当前时间戳来作为随机种子,然后随机一个小于26的数字, + 65 变成大写字母 ascii码值.
我们查看结果:

BBIKFUNZ
BBIKFUNZ
BBIKFUNZ
BBIKFUNZ
BBIKFUNZ
BBIKFUNZ
BBIKFUNZ
BBIKFUNZ
BBIKFUNZ
BBIKFUNZ

你测试上面代码时,可能得到的随机结果不是上面值,但是能确定的是,出来的随机数全部一样,
也许循环太快,可能随机种子相同了,但是你随机生成1万个验证码依然是一样的,感兴趣的同学可以试一下.
如果你碰到这样的业务逻辑,用上面的方式来生成随机数,就直接跳进坑里了.

这种情况怎么办呢,我们可以直接用循环变量来作为随机种子,
如下代码:

package main

import (
"fmt"
"math/rand"
)

func main() {

for i := 0; i < 10; i++ {
//当前循环变量作为随机种子
rand.Seed(int64(i))
//存放验证码的数组切片
bytes := make([]byte, 8)
for i := 0; i < 8; i++ {
//c的ascii码值:65~90为26个大写英文字母,97~122号为26个小写英文字母
//其余为一些标点符号、运算符号等
//我们要获取8个大写字母的验证码
b := rand.Intn(26) + 65
bytes[i] = byte(b)
}
fmt.Println(string(bytes))
}

}

我们直接查看运行结果:

CUBYHIZZ
XVLBZGBA
IMMOAGMS
MTUOPREV
NOFDHZOF
QMZSXIIF
QFWAGJGC
GOVHGGYY
AGZGUCHJ
ZSKNPCTQ

没有问题,上面的效果是我们想要的结果.