英文:
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)
}
如果你担心有人篡改你的系统时钟以破坏你的随机性,这也是一个不错的主意...然而,这只是一种更安全但仍然是伪随机性。
如果你有这种想法,你可能想使用密码学安全的随机数
希望能帮到你。
英文:
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
Hope that helps
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论