Golang中为什么rand.seed不起作用?

Golang中为什么rand.seed不起作用? 我认为以下代码应该总是生成相同的结果,因为在init函数中有rand.Seed来确保rand.Intn生成相同的随机整数序列。但实际上每次运行都会得到不同的结果。我是否误解了rand.Seed函数?

package main

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

var (
	wg sync.WaitGroup
)
func init(){
	rand.Seed(time.Now().UnixNano())
}

func main(){
	wg.Add(2)
	court := make(chan int)

	go play("James", court)
	go play("Yun", court)

	court <- 1

	wg.Wait()
}

func play(name string, court chan int){
	defer wg.Done()
	for {
		ball, ok := <- court
		if !ok {
			fmt.Printf("player %s won\n", name)
			return
		}
		n := rand.Intn(100)
		if n % 13 == 0{
			fmt.Printf("Player %s missed\n", name)
			close(court)
			return
		}
		fmt.Printf("Player %s Hit %d\n", name, ball)
		court <- ball + 1
	}

}

谢谢


更多关于Golang中为什么rand.seed不起作用?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

7 回复

感谢 @NobbZ 提供的详细解释!

更多关于Golang中为什么rand.seed不起作用?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


一旦你设法在相同的纳秒时间内设置种子,它就会生成相同的序列。但由于这种情况极不可能发生,所以每次的种子都是不同的。

有时你只是想用一个新的"半随机"种子重新初始化随机数生成器,以生成新的数字序列。

使用当前时间或从旧生成器中获取新值是常见的做法。

抱歉,RNG是什么意思?我对此还很陌生,不太清楚。你的意思是使用纳秒级时间作为半随机数吗?但实际上每次运行都会生成不同的结果,所以在我看来这完全是随机的,而不是半随机的。

很好,谢谢。

想知道作者为什么要这样做?我的意思是,如果作者想通过 rand.Seed 获得相同的结果,为什么他要使用总是生成不同结果的纳秒时间?这对我来说很奇怪……为什么不直接使用 rand.Seed(1) 呢?

rand.Seed(1)

RNG 是器。

当前时间戳的随机性因素在这里并不是关键,我们真正需要的是打破原有的可预测序列。

设想这样一个场景:

几年前有人编写了一个能够运行"小行星"街机版本的机器人程序,经过约2秒的游戏观察后,它就能计算出用于初始化RNG的种子值。从那一刻起,它甚至能够预判尚未生成的小行星动向。

定期使用新种子重新初始化RNG可以打破这种可预测性,使得预判未来状态变得更为困难。

但问题在于,如果继续使用旧序列中的新数值,观察者仍有可能推测出后续数值并延续之前的预测模式。而如果采用当前时间戳,由于观察者的系统时钟可能存在数纳秒的偏差,就无法确定精确数值——即便是单纳秒的差异也会产生完全不同的序列。

不过在某些场景下,可重现性反而是必要的。以多语言测试运行器为例,它们通常会打乱测试执行顺序。这类工具通常会输出所使用的种子值,并支持通过参数指定种子来复现之前的执行顺序(前提是测试用例数量未发生变动)。

另一个典型案例是程序化关卡生成器,例如在Roguelike类游戏中。开发者无需保存实际的关卡布局数据,只需存储种子值,在需要时即可重新生成对应关卡。

func main() {
    fmt.Println("hello world")
}

在您的代码中,rand.Seed 确实被调用了,但问题在于您使用了 time.Now().UnixNano() 作为种子值。由于 time.Now().UnixNano() 返回当前时间的纳秒级时间戳,每次运行程序时这个值都不同,导致随机数序列不重复。如果您希望每次运行生成相同的随机数序列,需要使用固定的种子值,例如一个常量整数。

以下是修改后的代码示例,使用固定种子值(如 42)来确保可重复的随机数序列:

package main

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

var (
	wg sync.WaitGroup
)

func init() {
	rand.Seed(42) // 使用固定种子值
}

func main() {
	wg.Add(2)
	court := make(chan int)

	go play("James", court)
	go play("Yun", court)

	court <- 1

	wg.Wait()
}

func play(name string, court chan int) {
	defer wg.Done()
	for {
		ball, ok := <-court
		if !ok {
			fmt.Printf("player %s won\n", name)
			return
		}
		n := rand.Intn(100)
		if n%13 == 0 {
			fmt.Printf("Player %s missed\n", name)
			close(court)
			return
		}
		fmt.Printf("Player %s Hit %d\n", name, ball)
		court <- ball + 1
	}
}

在这个版本中,每次运行程序都会生成相同的输出,因为种子是固定的。如果您需要基于时间的随机性(例如在游戏或模拟中),保留 time.Now().UnixNano() 是正确的;否则,使用固定种子用于测试或可重复场景。

回到顶部