Zulip Chat Archive
Stream: new members
Topic: inhabited instance for StdGen isn't very random
Michael MacLeod (Aug 07 2025 at 00:23):
I am very confused by the fact that stdNext seems to return its input unchanged when its input is (default : StdGen). Is this really the intended behavior?
I ran into this problem when I was having trouble producing different random numbers using randNat.
#eval do
let gen : StdGen := default
let (a, gen) := randNat gen 0 10000
let (b, gen) := randNat gen 0 10000
let (c, gen) := randNat gen 0 10000
let (d, gen) := randNat gen 0 10000
let (e, gen) := randNat gen 0 10000
let (f, _) := randNat gen 0 10000
IO.println s!"{a}" -- 8835
IO.println s!"{b}" -- 8835
IO.println s!"{c}" -- 8835
IO.println s!"{d}" -- 8835
IO.println s!"{e}" -- 8835
IO.println s!"{f}" -- 8835
I expected that calling randNat successively would produce different numbers, but it instead produces the same number, 8835. As far as I can tell, randNat returns the same generator it received. A little experimentation shows that when stdNext is passed default, we get default back, which I believe is the culprit here:
#eval do
let gen : StdGen := default
let (_, gen') := stdNext gen
if gen.s1 ≠ gen'.s1 ∨ gen.s2 ≠ gen'.s2 then
return "Recieved a different generator"
return s!"Received the same generator"
(this evaluates to "Received the same generator")
Here's a better proof:
Lean 4 Web example
theorem instInhabitedStdGen.not_very_random : (stdNext default).snd = default := by
simp [stdNext, instInhabitedStdGen]
Aaron Liu (Aug 07 2025 at 00:31):
The default StdGen is ⟨0, 0⟩
Kyle Miller (Aug 07 2025 at 00:31):
Looking at mkStdGen, it suggests that the generator should have nonzero state, but default sets the state to 0.
Aaron Liu (Aug 07 2025 at 00:32):
you should input a random generator instead
Kyle Miller (Aug 07 2025 at 00:32):
How about as a workaround, use mkStdGen?
I'm not sure that the Inhabited instance is meant to be the way you create generators (but I don't know that it's not)
Kyle Miller (Aug 07 2025 at 00:33):
Are you using default intentionally, or is default being used because of something else, like a derived Inhabited instance?
Michael MacLeod (Aug 07 2025 at 00:35):
Yes, the problem is what Aaron said about ⟨0, 0⟩ returning itself when passed to stdNext. If you use mkStdGen then this avoids the issue.
I was using it intentionally to test some code out I was working on. I just expected that the default instance of a random number generator would actually function as a random number generator, not a constant number generator. (is there a good reason to have the default instance be ⟨0, 0⟩?) I get that zero is a nice value to use as a default, but using it breaks the randomness.
Robin Arnez (Aug 07 2025 at 08:52):
It's even explicitly defined!
instance : Inhabited StdGen := ⟨{ s1 := 0, s2 := 0 }⟩
Changing this to
instance : Inhabited StdGen := ⟨{ s1 := 1, s2 := 1 }⟩
would probably fix the problems.
Robin Arnez (Aug 07 2025 at 09:37):
Last updated: Dec 20 2025 at 21:32 UTC