Random Bool
すごいHaskell P.340 に State モナドを使って乱数の生成をきれいに書く例が載っている。
import System.Random import Control.Monad.State randomSt :: (RandomGen g, Random a) => State g a randomSt = state random threeConins :: State StdGen (Bool, Bool, Bool) threeConins = do a <- randomSt b <- randomSt c <- randomSt return (a, b, c)
これを何度か実行してみる。
*Main> runState threeConins $ mkStdGen 0 ((True,True,True),1346387765 2103410263) *Main> runState threeConins $ mkStdGen 1 ((True,False,True),545291967 2103410263) *Main> runState threeConins $ mkStdGen 2 ((True,True,False),1891679732 2103410263) *Main> runState threeConins $ mkStdGen 3 ((True,True,False),1090583934 2103410263) *Main> runState threeConins $ mkStdGen 4 ((True,False,False),289488136 2103410263)
ランダムな値が返ってきているが、先頭の値が毎回 True になっている。先頭の値がはじめて False になるシードの値を調べてみたくなった。調べる為に以下の関数を作成した。
headTrueSeeds :: [Int] headTrueSeeds = takeWhile (fst3 . (evalState threeConins) . mkStdGen) [0..] where fst3 (a, _, _) = a
実行してみる。
*Main> last headTrueSeeds 53667
53667 までずっと先頭がTrue のパターンが続いている。というわけで、先頭の値がはじめて False になるシードの値は 53668。
どうしてこうなるかを調べてみる。
Bool のランダム値は以下のように生成される。random を適用する g が (mkStdGen 53668) の時にはじめて x が 0 になる (上で調べた結果から)。
instance Random Bool where randomR (a,b) g = case (randomIvalInteger (bool2Int a, bool2Int b) g) of (x, g') -> (int2Bool x, g') where bool2Int :: Bool -> Integer bool2Int False = 0 bool2Int True = 1 int2Bool :: Int -> Bool int2Bool 0 = False int2Bool _ = True random g = randomR (minBound,maxBound) g
ということは x を決めている randomIvalInteger に秘密がありそうなので、調べていくとその中で使用している next 関数が重要な役割を持っているようだ。以下は next の定義。
instance RandomGen StdGen where next = stdNext 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'
53668 という数がある。random 値を生成した結果が False になる為には、z' が 0 にならなければいけない。その為には z が 0 でなければならない。その他には s1'' と s2'' が等しくならなければいけない。その為には ... ...
全て追ったわけではないのだが、ここの 53668 が影響しているのは確かだろう(多分ね〜)。