我试图精确地理解为什么从外部函数调用时,我的时间种子随机数生成器会返回相同数字的序列。
问题的最小工作示例:
package main
import (
"fmt"
"math/rand"
"time"
)
//Generates random int as function of range
func getRand(Range int) int {
s1 := rand.NewSource(time.Now().UnixNano())
r1 := rand.New(s1)
return r1.Intn(Range)
}
//Print 100 random ints between 0 and 100
func main() {
for i := 0; i < 100; i++ {
fmt.Print(getRand(100), ", ")
}
}
此输出为
Out[1]: 40, 40, 40, 40, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 47,
47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
47,47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99,
我想知道为什么这种情况发生在我本人的教育中。我也欢迎提出解决方案的建议。
详细信息:我需要在代码的许多外部函数中调用随机数,但像MWE这样,当在main以外的函数中进行种子植入时,它们会反复返回相同的数字。此外,我需要动态更新范围,因此无法生成先验列表。我宁愿不必在main()中生成数字并将其传递给每个函数-范围是在这些函数内部计算的,这会使事情复杂化
答案 0 :(得分:6)
这是因为time.Time
的粒度(1纳秒)就像您的系统时钟一样(甚至可能是几毫秒,取决于许多情况),并且如果您多次调用time.Now()
在更大的粒度范围内,返回的time.Time
很有可能是相同的,这意味着其Time.UnixNano()
方法将为您返回相同的纳秒(相同的数字)。
如果您使用与种子相同的数字,则随机数生成器应返回相同的数字。
您只需在应用启动时为RNG播种一次,而无需在每次使用前播种。您可以为此使用包init()
或在变量声明中使用该函数:
var r = rand.New(rand.NewSource(time.Now().UnixNano()))
//Generates random int as function of range
func getRand(Range int) int {
return r.Intn(Range)
}
//Print 100 random ints between 0 and 100
func main() {
for i := 0; i < 100; i++ {
fmt.Print(getRand(100), ", ")
}
}
示例输出(在Go Playground上尝试):
0、28、27、62、63、89、24、27、88、84、82、55、49、35、2、32、84、58、78、28、26、58、30、28 ,74,6,39,24,40,47,49,39,61,62,67,7,94,87,37,99,90,80,93,83,27,69,25,45,99 ,12、44、39、34、86、18、42、76、40、44、12、70、3、70、99、57、43、90、65、97、64、68、60、65、56 ,3、81、54、56、43、57、92、93、54、92、9、86、16、72、29、12、97、87、55、42、87、41、94、53、23 ,64,
这里要注意的一件事:rand.NewSource()
返回的源不能安全地并发使用。如果您需要从多个goroutine中调用getRand()
,则需要同步对r
的访问,或者在每个goroutine中使用单独的rand.Rand
。
答案 1 :(得分:1)
我不是Go专家,但我认为该问题是通用编程问题。这与您为每个调用设置种子的事实有关。种子基于时间函数。因此,发生的事情是,在很短的时间内,您有许多呼叫,而时间没有更改(尚未),因此您获得了相同的值,因为您一次又一次地设置了相同的种子。
尝试设置种子,仅在调用for
循环中一次和外部。