在Go语言的官方Tour中,如何初始化随机数生成器?

huangapple go评论86阅读模式
英文:

How do I Seed random number generator on official Tour of Go?

问题

官方的Go语言教程在沙盒中给出了以下代码:

package main

import (
	"fmt"
	"math/rand"
)

func main() {
	fmt.Println("我的最爱的数字是", rand.Intn(10))
}

并且给出了以下说明:

注意:这些程序执行的环境是确定性的,所以每次运行示例程序时,rand.Intn将返回相同的数字。(要看到不同的数字,请给随机数生成器设置种子;参见rand.Seed。)

在阅读了官方文档中关于rand.Seed的条目以及阅读了这个答案之后,我仍然无法正确地给随机数生成器设置种子。

有人能演示一下如何使用rand.Seed函数来给随机数生成器设置种子吗?

非常感谢!
Jon

英文:

The official tour of Go gives the following code in the sandbox:

package main

import (
	"fmt"
	"math/rand"
)

func main() {
 	fmt.Println("My favorite number is", rand.Intn(10))
}

And this instruction:

>Note: the environment in which these programs are executed is deterministic, so each time you run the example program rand.Intn will return the same number. (To see a different number, seed the number generator; see rand.Seed.)

After reading the entry under the official documentation for rand.Seed and reading this answer, I still can't correctly seed the random number generator.

Can someone please demonstrate how the rand.Seed function should be used to seed the random number generator?

Many thanks,
Jon

答案1

得分: 12

默认情况下,rand.Intn使用全局随机数生成器globalRand.Intn。它在内部创建,可以参考这里。所以当你通过rand.Seed设置种子值时:

rand.Seed(time.Now().UTC().UnixNano())

然后globalRand会使用新的种子值。

当需要时,你可以使用特定的种子值创建自己的随机数生成器。可以参考godoc示例


Play链接(无种子):https://play.golang.org/p/2yg7xjvHoJ

输出:

我最喜欢的数字是1
我最喜欢的数字是7
我最喜欢的数字是7
我最喜欢的数字是9
我最喜欢的数字是1
我最喜欢的数字是8
我最喜欢的数字是5
我最喜欢的数字是0
我最喜欢的数字是6

Play链接(有种子):https://play.golang.org/p/EpW6R5rvM4

输出:

我最喜欢的数字是0
我最喜欢的数字是8
我最喜欢的数字是7
我最喜欢的数字是2
我最喜欢的数字是3
我最喜欢的数字是9
我最喜欢的数字是4
我最喜欢的数字是7
我最喜欢的数字是8

编辑:

正如@AlexanderTrakhimenok提到的,在Playground中程序的执行是“确定性的”。然而,Playground并不会阻止你提供rand.Seed的值。

记住,种子值是int64类型。

当你调用rand.Intn时,它使用默认种子值1作为globalRand的种子。

var globalRand = New(&lockedSource{src: NewSource(1).(Source64)})

在Playground中,time.Now().UTC().UnixNano()会给出相同的值1257894000000000000,因为“开始时间被锁定为一个常量”。但它与默认种子值不同,这就是为什么第二个Playground链接产生不同结果的原因

所以上述两个链接总是产生相同的结果。

我们如何在Playground中改变结果?

是的,我们可以。让我们将UnixNano()的值1500909006430687579作为rand.Seed的参数,这个值是从我的机器生成的。

Play链接:https://play.golang.org/p/-nTydej8YF

输出:

我最喜欢的数字是3
我最喜欢的数字是5
我最喜欢的数字是3
我最喜欢的数字是8
我最喜欢的数字是0
我最喜欢的数字是5
我最喜欢的数字是4
我最喜欢的数字是7
我最喜欢的数字是1
英文:

By default rand.Intn uses the globalRand.Intn. Its created internally, refer here. So when you set via rand.Seed

rand.Seed(time.Now().UTC().UnixNano())

Then globalRand uses the new seed value.

When needed you can create your own rand generator with seed value. Refer to godoc example.


Play Link (without seed): https://play.golang.org/p/2yg7xjvHoJ

Output:

My favorite number is 1
My favorite number is 7
My favorite number is 7
My favorite number is 9
My favorite number is 1
My favorite number is 8
My favorite number is 5
My favorite number is 0
My favorite number is 6

Play Link (with seed): https://play.golang.org/p/EpW6R5rvM4

Output:

My favorite number is 0
My favorite number is 8
My favorite number is 7
My favorite number is 2
My favorite number is 3
My favorite number is 9
My favorite number is 4
My favorite number is 7
My favorite number is 8

EDIT:

As @AlexanderTrakhimenok mentioned, in playground program execution is deterministic. However, the playground doesn't stop you from supplying rand.Seed value.

Remember Seed value is int64.

When you rand.Intn, it uses default seed value 1 for globalRand.

var globalRand = New(&lockedSource{src: NewSource(1).(Source64)})

And in playground time.Now().UTC().UnixNano() gives you same value 1257894000000000000 since the start time is locked to a constant. But it is different from default seed value, that's why second playground link produces the different result.

So above two would produce the same result always.

How should we change the result in playground?

Yes, we can. Let's supply UnixNano() value 1500909006430687579 to rand.Seed, which is generated from my machine.

Play Link: https://play.golang.org/p/-nTydej8YF

Output:

My favorite number is 3
My favorite number is 5
My favorite number is 3
My favorite number is 8
My favorite number is 0
My favorite number is 5
My favorite number is 4
My favorite number is 7
My favorite number is 1

答案2

得分: 5

根据你的引述:

> 这些程序执行的环境是确定性的。

因此,Go Playground 的设计不允许创建真正的伪随机输出。

这是有意为之的,目的是为了缓存结果,以减少后续运行时的CPU/内存使用。因此,引擎只需评估你的程序一次,并在每次你或其他人再次运行时提供相同的缓存输出。

出于同样的目的,起始时间被锁定为一个常数。

你可能想阅读一篇关于为什么以及如何以这种方式实现的博客文章:https://blog.golang.org/playground

英文:

As you quoted yourself:

> the environment in which these programs are executed is deterministic.

So the Go Playground by design does not allow to create truly pseudo-random outputs.

This is done intentionally for purpose of caching results to minimize CPU/memory usage for consequent runs. So the engine can evaluate your program just once and serve the same cached output every time when you or anyone else run it again.

For the same purpose the start time is locked to a constant.

You may want to read a blog post on how and why it's implemented this way: https://blog.golang.org/playground

答案3

得分: 0

对于那些真正需要在Go Playground中使用伪随机种子的人来说,不要使用恒定的时间戳。你可以访问执行内存使用情况,即使在沙箱中,每次执行都会略有不同。因此,你的种子可以是非确定性的,实际上有些伪随机,就像这样:

package main

import (
	"fmt"
	"math/rand"
	"strings"
	"time"
	"github.com/shirou/gopsutil/mem"
)

var extraSeed int64 = 1

func main() {
	memStat, err := mem.VirtualMemory()
	if err == nil {
		extraSeed = (int64)(memStat.Used)
	}
	fmt.Println("额外种子:", extraSeed)
    
	rand.Seed(time.Now().UnixNano() + extraSeed) //<---- 
	// 即使在Go Playground中,额外种子也会随机工作,
    // 因为每次执行时内存使用情况最终都会略有不同
	
	// 做一些随机操作,例如洗牌
	orderedList := []string{"a","b","c","d","e"}
	
	rand.Shuffle(len(orderedList), func(i, j int) {
		orderedList[i], orderedList[j] = orderedList[j], orderedList[i]
	})
	fmt.Println("这应该是随机洗牌的结果:", orderedList)
	
}

如果你担心有人篡改你的系统时钟以破坏你的随机性,这也是一个不错的主意...然而,这只是一种更安全但仍然是伪随机性。

如果你有这种想法,你可能想使用密码学安全的随机数 在Go语言的官方Tour中,如何初始化随机数生成器?

希望能帮到你。

英文:

For anyone who really needs a pseudo-random seed in go playground, just don't use the constant timestamp. You can access the execution memory usage which is slightly different on each execution in go playground even in the sandbox. So your seed can very well be non-deterministic and actually somewhat pseudo-random like this:

package main

import (
	"fmt"
	"math/rand"
	"strings"
	"time"
	"github.com/shirou/gopsutil/mem"
)

var extraSeed int64 = 1

func main() {
	memStat, err := mem.VirtualMemory()
	if err == nil {
		extraSeed = (int64)(memStat.Used)
	}
	fmt.Println("extra seed : ", extraSeed)
    
	rand.Seed(time.Now().UnixNano() + extraSeed) //<---- 
	// extraSeed will work randomly even in go playground,
    // since mem-Usage is eventually slightly different each time we execute it
	
	
	// do something random, for example shuffle
	orderedList := []string{"a","b","c","d","e"}
	
	rand.Shuffle(len(orderedList), func(i, j int) {
		orderedList[i], orderedList[j] = orderedList[j], orderedList[i]
	})
	fmt.Println("This should be random shuffeled: ", orderedList)
	
}

Maybe this is also a good idea if you fear that someone is tempering with your system clock to break your randomness... However that is just a more safe but still a pseudo-randomness.

If you have this kind of thoughts you might want to use cryptography safe random numbers instead 在Go语言的官方Tour中,如何初始化随机数生成器?

Hope that helps

huangapple
  • 本文由 发表于 2017年7月24日 03:15:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/45268998.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定