2017年3月

golang随机数有一个很好玩的地方,如果我们不自行定义随机数种子的话,每次的随机数都是一样的。


比如我们for循环,取8个随机数,每次运行结果是一模一样的。代码如下:


package main


import (

    "fmt"

    "math/rand"

)


var c chan int


func product(){

    r := rand.Intn(10)

    fmt.Println("随机数: ", r)

    c <- r

}


func main(){

    c = make(chan int, 8)

    for i:= 0; i<8; i++{

        go product()

    }

    total := 0

    for i:= 0; i<8; i++{

        total += <- c

    }

    fmt.Println("总和: ", total)

}


结果如下:

➜  src go run index.go

随机数:  9

随机数:  7

随机数:  7

随机数:  1

随机数:  8

随机数:  5

随机数:  1

随机数:  0

总和:  38

➜  src go run index.go

随机数:  7

随机数:  8

随机数:  5

随机数:  1

随机数:  0

随机数:  1

随机数:  9

随机数:  7

总和:  38

➜  src go run index.go

随机数:  1

随机数:  8

随机数:  1

随机数:  5

随机数:  7

随机数:  0

随机数:  7

随机数:  9

总和:  38

➜  src go run index.go

随机数:  7

随机数:  1

随机数:  9

随机数:  1

随机数:  8

随机数:  5

随机数:  0

随机数:  7

总和:  38

➜  src go run index.go

随机数:  7

随机数:  9

随机数:  1

随机数:  8

随机数:  0

随机数:  5

随机数:  1

随机数:  7

总和:  38

➜  src go run index.go

随机数:  7

随机数:  1

随机数:  1

随机数:  9

随机数:  8

随机数:  5

随机数:  0

随机数:  7

总和:  38


可以看出,每次随机数都是0、1、1、5、7、7、8、9,总和一直都是38。因为使用了go关键字,所以顺序不同,但是如果去掉go关键字,改为单去程的话,那真就是顺序和值都完全一模一样了。


为什么?


我们打开golang的源码,可以看到:


rand.Int()实际是func Int() int { return globalRand.Int() }

而globalRand是var globalRand = New(&lockedSource{src: NewSource(1)})

NewSource又是

func NewSource(seed int64) Source {

    var rng rngSource rng.Seed(seed) 

    return &rng 

}


也就是说,每次在默认随机的时候,golang是固定了以数字1作为种子,进行随机。那种子都固定了的话,每次执行的时候结果当然是一样的了。


而要解决这个问题,就需要以时间作为随机数种子。代码如下:


package main


import (

    "fmt"

    "math/rand"

    "time"

)


var c chan int


func product(){

    rand.Seed(int64(time.Now().Nanosecond()))

    r := rand.Intn(10)

    fmt.Println("随机数: ", r)

    c <- r

}


func main(){

    c = make(chan int, 8)

    for i:= 0; i<8; i++{

        go product()

    }

    total := 0

    for i:= 0; i<8; i++{

        total += <- c

    }

    fmt.Println("总和: ", total)

}


这时再运行,就会发现比较“随机”了。


➜  src go run index.go

随机数:  8

随机数:  1

随机数:  3

随机数:  9

随机数:  6

随机数:  8

随机数:  5

随机数:  7

总和:  47

➜  src go run index.go

随机数:  0

随机数:  5

随机数:  1

随机数:  3

随机数:  2

随机数:  3

随机数:  5

随机数:  1

总和:  20

➜  src go run index.go

随机数:  2

随机数:  9

随机数:  8

随机数:  2

随机数:  5

随机数:  1

随机数:  9

随机数:  0

总和:  36

➜  src go run index.go

随机数:  4

随机数:  1

随机数:  0

随机数:  4

随机数:  6

随机数:  2

随机数:  8

随机数:  8

总和:  33

➜  src go run index.go

随机数:  0

随机数:  3

随机数:  5

随机数:  1

随机数:  1

随机数:  6

随机数:  5

随机数:  6

总和:  27


而PHP作为最好的语言,这点还是比较人性化的,PHP默认就是以时间作为种子的。在文档中有这么一句话:

Note: 自 PHP 4.2.0 起,不再需要用 srand() 或 mt_srand() 给随机数发生器播种 ,因为现在是由系统自动完成的。