关于golang的rand包的问题

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

Questions about golang's rand package

问题

我一直在使用和阅读关于Golang的rand包,但我找不到一个真正解释它在我能理解的方式中做了什么的资源。

首先,当你"seed"时,实际上发生了什么?如果你随后在一个范围内请求一个随机数(例如使用rand.Intn(7)),似乎这应该足够告诉Go你要找的是什么,但事实并非如此。rand.Seed()贡献了什么?

这是一个你可以玩耍的示例:

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

我在想也许"Source"是你可以从中获取的所有可能数字,然后类似rand.Intn()这样的函数从Source的子集中给你一个随机数。所以种子只是定义了Source。但是,如果我尝试从初始种子范围之外获取一个随机数,它仍然有效,所以这似乎不正确,除非它只是排除了不存在的范围部分。看看这个例子:

func main() {
  rand.Seed(6)
  fmt.Println(rand.Intn(7))
}

还有关于文档中的这一行:

如果没有调用Seed函数,生成器将在程序启动时随机种子。

这似乎并没有发生,因为如果你不调用rand.Seed()并尝试使用rand.Intn(),你只会一遍又一遍地得到相同的结果,类似于使用常数与rand.Seed()一起使用。

其次,rand.Intn()的文档中说它"如果n <= 0,会引发panic"。我使用上面的链接进行了测试,这是不正确的,它会返回0而不会引发panic。实际上,我想包括0,所以这是好的,但它与文档中的说法不一致。在指定范围时,是否有另一种正确的方法来包括0?我找不到。

第三,文档中提到了一个"默认Source"。那只是存在而无需我们做任何事情吗?然后,如果我想要一个特定的Source,我使用rand.New()来创建一个?为什么我需要一个特定的Source?

Go的文档被认为非常好而且经过深思熟虑,所以我想我可能在这里漏掉了什么,感谢任何人的见解!

英文:

I've been using and reading about the Golang rand package but I can't find a resource that actually explains what it's doing in a way I can understand.

First, what is really happening when you "seed"? If you are then asking for a random number in a range (for example using rand.Intn(7)), it seems like that should be enough to tell Go what you're looking for, but it's not. What does rand.Seed() contribute?

Here is an example that you can play with here:

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

I was thinking maybe the "Source" is all possible numbers you could pull from and then something like rand.Intn() gives you a random number from a subset of the Source. So seeding is just defining the Source. But if I try getting a random number from a range outside of the initial seed, it stills works, so that doesn't seem to be right, unless it just excludes the part of the range that doesn't exist. See this example:

func main() {
  rand.Seed(6)
  fmt.Println(rand.Intn(7))
}

And what about this line in the documentation that says:

> If Seed is not called, the generator is seeded randomly at program startup.

That doesn't seem to happen because if you don't call rand.Seed() and try to use rand.Intn(), you just get the same thing over and over, similar to if you use a constant with rand.Seed().

Second, the documentation for rand.Intn() says it "panics if n <= 0". I've tested this using the above link and it's not true, it will return 0 without panicking. I actually want to include 0 so this is good but it's not lining up with what the docs say. Is there another, proper way to include 0 when specifying a range? I couldn't find one.

Third, the documentation talks about a "default Source" here. Is that just something that exists without us having to do anything? And then if I want a specific Source I use rand.New() to create one? Why would I want a specific one?

Go's documentation is considered very good and well thought out so I think I must just be missing something here, appreciate anyone's insight!

答案1

得分: 3

math/rand随机数生成器是一个“伪”随机数生成器。从概念上讲,它的工作原理如下:

var last int

func nextRandom() int { 
   last = computeRandom(last)
   return last
}

因此,它是一个确定性函数,根据前一个值生成一系列的值。给定一个初始值last,连续调用nextRandom将生成相同的值序列。

Seed函数设置了last的值,因此通过将最后一个值设置为某个数字,比如当前时间,你可以得到一个不同的数字序列。

截至本文撰写时(v1.19),随机数生成器在程序启动时不会使用随机数进行种子化。这似乎在v1.20中发生了变化。

n<=0时,Intn函数会引发恐慌:https://cs.opensource.google/go/go/+/refs/tags/go1.20.2:src/math/rand/rand.go;l=178

默认的源是一个包级别的Source实例,被所有导出函数使用:

var globalRand = New(new(lockedSource))

func Int63() int64 { return globalRand.Int63() }
...
英文:

The math/rand random number generator is a "pseudo" random number generator. Conceptually, it looks like this:

var last int

func nextRandom() int { 
   last:= computeRandom(last)
   return last
}

So it is a deterministic function that generates a sequence of values based on the previous value. Given an initial value of last, consecutive calls to nextRandom will generate the same sequence of values.

Seed sets the last value, so by setting the last value to some number, say, current time, you get a different sequence of numbers.

As of this writing (v1.19), the random number generator is not seeded with a random number at program startup. This seems to have changed at v1.20.

Intn panics when n&lt;=0: https://cs.opensource.google/go/go/+/refs/tags/go1.20.2:src/math/rand/rand.go;l=178

The default source is a package level Source instance used by all exported functions:

var globalRand = New(new(lockedSource))

func Int63() int64 { return globalRand.Int63() }
...

答案2

得分: 0

rand.Seed()的作用是设置伪随机数生成器的状态。伪随机数生成器会根据该状态生成随机性。如果多次设置种子,则后面的种子值会被混合在一起。

我在想,"Source"可能是你可以从中获取所有可能数字的集合,然后rand.Intn()之类的函数会从该集合中随机选择一个数字。

实际上,"Source"是一个有限的随机数生成器,它只产生位(或者在这种情况下,范围在[0, 2^63)的整数值,这相当于63位)。然后可以使用这些位执行其他任务,比如生成其他范围的数字,或者对列表进行洗牌等。

这似乎是最近的更改。所以我猜你的运行时和文档不是同一个API版本。

rand.Intn()的文档中说,如果n <= 0,会引发panic。根据我测试的结果,这似乎是错误的,它会返回0而不会引发panic。

这听起来像是一个bug。如果n确实是0或小于0,那么它应该引发panic。

我实际上想要包括0,所以这很好,但它与文档中的描述不一致。在指定范围时,是否有另一种正确的方法来包括0?我找不到其他方法。

我不确定你的意思是什么。如果你指定0,那么你得到的范围是[0, 0),这没有任何意义,因为0作为起始值被包括在内,然后作为结束值被排除在外。你可能会认为[0, 1)也是有问题的,因为只返回0并不是很随机(作为API设计者,我仍然允许这种情况,但是是的,这是一个退化的情况)。

rand.Intn()生成的范围是[0, n)。无论n是什么,0都会包含在可能的输出中。

文档中提到了一个"默认的Source"。这只是一个无需我们做任何操作就存在的东西吗?如果我想要一个特定的Source,我使用rand.New()来创建一个?为什么我需要一个特定的Source?

也许你想创建一个非常特定的测试值序列,不希望它发生变化,比如用于测试目的。

英文:

> What does rand.Seed() contribute?

Seeding a pseudo random number generator means providing input that sets it state. A pseudo random number generator then creates randomness from that state. If you seed the PRNG multiple times then the later seed values get mixed in.

> I was thinking maybe the "Source" is all possible numbers you could pull from and then something like rand.Intn() gives you a random number from a subset of the Source.

The Source is really a limited RNG in the sense that it only produces bits (or, in this case, integer values in the range [0, 2^63), which of course simply corresponds with 63 bits. That can then be used to perform other jobs, such as generating numbers in other ranges, or maybe to shuffle a list, etc.

> That doesn't seem to happen because if you don't call rand.Seed() and try to use rand.Intn(), you just get the same thing over and over, similar to if you use a constant with rand.Seed().

This seems to be a recent change. So I guess your runtime and documentation are not for the same API version.

> Second, the documentation for rand.Intn() says it "panics if n <= 0". I've tested this using the above link and it's not true, it will return 0 without panicking.

Sounds like a bug to me. If n is indeed 0 or lower than 0 then it should panic.

> I actually want to include 0 so this is good but it's not lining up with what the docs say. Is there another, proper way to include 0 when specifying a range? I couldn't find one.

Not sure what you mean here. If you'd specify 0 then you get the range [0, 0) which doesn't make any sense, as 0 is first included as start value, and then excluded as end value. You'd expect [0, 1) also to be problematic as just returning 0 is not very random (I'd still allow for the function as API designer, but yeah, it's a degenerate case).

rand.Intn() generates a range [0, n). Whatever n is going to be, 0 is going to be included in the possible output.

> Third, the documentation talks about a "default Source" here. Is that just something that exists without us having to do anything? And then if I want a specific Source I use rand.New() to create one? Why would I want a specific one?

Well, maybe you want to create a very specific run of test values that doesn't change, e.g. for testing purposes.

huangapple
  • 本文由 发表于 2023年3月30日 06:55:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/75882826.html
匿名

发表评论

匿名网友

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

确定