Temporal correlations when employing System.Random (not present when employing System.Random.TF)

后端 未结 1 388
故里飘歌
故里飘歌 2021-01-18 10:29

This question concerns the origins of temporal correlations one observes with System.Random when one generates successive randoms from successive seeds (where o

相关标签:
1条回答
  • 2021-01-18 10:47

    Let's begin by looking at what the code says, and we can get there.

    First off, random as applied to Bool is equivalent to:

    randomB :: StdGen -> (Bool, StdGen)
    randomB g = let (i, g') = next g in (i `mod` 2 == 1, g')
    

    In fact, if I replace random with randomB where that's appropriate in your program, I get identical results. The point is that for determining booleans, all we care about is whether the next Int value is even or odd.

    Next, let's look at the definition of StdGen:

    data StdGen 
     = StdGen Int32 Int32
    

    So two Int32s are the state. Let's see how they're initialized with mkStdGen and how they're adjusted with next:

    mkStdGen :: Int -> StdGen -- why not Integer ?
    mkStdGen s = mkStdGen32 $ fromIntegral s
    
    mkStdGen32 :: Int32 -> StdGen
    mkStdGen32 s
     | s < 0     = mkStdGen32 (-s)
     | otherwise = StdGen (s1+1) (s2+1)
          where
        (q, s1) = s `divMod` 2147483562
        s2      = q `mod` 2147483398
    

    ...

    stdNext :: StdGen -> (Int, StdGen)
    -- Returns values in the range stdRange
    stdNext (StdGen s1 s2) = (fromIntegral z', StdGen s1'' s2'')
        where   z'   = if z < 1 then z + 2147483562 else z
            z    = s1'' - s2''
    
            k    = s1 `quot` 53668
            s1'  = 40014 * (s1 - k * 53668) - k * 12211
            s1'' = if s1' < 0 then s1' + 2147483563 else s1'
    
            k'   = s2 `quot` 52774
            s2'  = 40692 * (s2 - k' * 52774) - k' * 3791
            s2'' = if s2' < 0 then s2' + 2147483399 else s2'
    

    Note two interesting things:

    1. The way s2 is initialized guarantees that it will be 1 unless you send a very high number to mkStdGen, in which case it will be 2 (there are fewer than 200 values in the Int32 range that will initialize s2 to 2.

    2. The two halves of the state are kept distinct - the updated s2 depends only on the previous s2, not on the previous s1, and vice versa.

    As a consequence, if you examine the generators that you get out of a certain fixed number of generations passed to better_mkStdGen, the second half of their state will always be identical.

    Try it by adding this to your program:

    print $ map (dropWhile (/= ' ') . show . better_mkStdGen 10) [0 .. 20]
    

    So then the question is, why the mixing function in s1 fails to mix the last bit properly. Note that the way it's written, s1' and k will have the same parity as s1, so the s1 state only has different parity from the previous s1 state if s1' ends up being less than zero.

    At this point I need to handwave a bit and say that the way s1' is computed means that if two initial values for s1 are close to each other, the two values for s1' will also be close together, and in general will only be 40014 times as far apart as they were initially, which in the range we allow for s1 makes neighboring values quite likely to end up on the same side of zero.

    0 讨论(0)
提交回复
热议问题