您的位置:

探究Go语言的随机数

一、随机数的概念

随机数是计算机中常使用的一种数值,它的产生具有不可预知性。随机数广泛应用于加密、模拟和统计学等领域。在Go语言中,随机数可以用math/rand包实现,利用各种方法使随机性进一步增强。

二、伪随机数生成器PRNG

在Go语言中,math/rand包提供了伪随机数生成器的函数,其中最常用的就是Intn(n)函数,可以产生一个[0,n)的伪随机整数。PRNG以种子值作为初始值,根据数学公式计算不同的随机数。由于具有确定性,所以称为伪随机数。

package main

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

func main() {
	rand.Seed(time.Now().UnixNano())
	fmt.Println(rand.Intn(10))
}

运行上述代码,输出的结果是0~9的伪随机整数,由于使用了UnixNano()将纳秒级别的时间戳作为种子值,保证每次生成的随机数都不同。可以通过Seed()函数手动设置种子值。

三、真随机数生成器

相较于伪随机数,真随机数是真正的随机数,能够保证具有不可预知性。Go语言中提供了crypto/rand包,可以用于生成真随机数。

package main

import (
	"crypto/rand"
	"fmt"
	"math/big"
)

func main() {
	n, err := rand.Int(rand.Reader, big.NewInt(100))
	if err != nil {
		fmt.Println("出错了")
	}
	fmt.Println(n)
}

运行上述代码,将产生一个范围在0~99的真随机整数。在生成真随机数时,必须指定特定的种子值,这里使用了cryptographic random generator实现安全产生真随机数。

四、使用多个goroutine

在并行计算中,需要产生多个随机数,每个goroutine都需要产生不同的随机数,可以使用rand.New()函数为每个goroutine创建一个独立种子的PRNG。

package main

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

func worker(id int, wg *sync.WaitGroup) {
	defer wg.Done()

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

	for i := 0; i < 5; i++ {
		fmt.Printf("Worker %d: %d\n", id, r.Intn(100))
		time.Sleep(time.Millisecond * 100)
	}
}

func main() {
	var wg sync.WaitGroup

	for i := 0; i < 5; i++ {
		wg.Add(1)
		go worker(i, &wg)
	}

	wg.Wait()
}

运行上述代码,会输出5个goroutine生成的随机数。这里每个goroutine都使用了一个独立的PRNG,因此产生的随机数的序列会不同。

五、结合时间的使用

在Go语言中,可以将时间结合到随机数产生中,可以根据时间戳计算出不同的种子值,并将其用作PRNG的初始值。

package main

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

func main() {
	rand.Seed(time.Now().UnixNano())

	for i := 0; i < 5; i++ {
		fmt.Println(rand.Intn(100))
		time.Sleep(time.Millisecond * 100)
	}
}

运行上述代码,每隔100毫秒将会产生一个0~99范围内的随机数。这里使用UnixNano()函数产生纳秒级别的时间戳,作为种子值,每次运行程序将产生不同的随机数序列。

六、总结

Go语言中提供了math/rand和crypto/rand包,分别用于产生伪随机数和真随机数。在多个goroutine环境下,可以为每个goroutine创建独立的PRNG来产生随机数。时间也可以结合随机数产生中,利用时间戳产生不同的种子值,进一步增强随机数生成的随机性。