1 % ------------------------------------------------------------------------------
2 % $Id: Random.lhs,v 1.23 2001/02/22 16:48:24 qrczak Exp $
4 % (c) The University of Glasgow, 1995-2000
7 \section[Random]{Module @Random@}
9 The June 1988 (v31 #6) issue of the Communications of the ACM has an
10 article by Pierre L'Ecuyer called, "Efficient and Portable Combined
11 Random Number Generators". Here is the Portable Combined Generator of
12 L'Ecuyer for 32-bit computers. It has a period of roughly 2.30584e18.
14 Transliterator: Lennart Augustsson
16 sof 1/99 - code brought (kicking and screaming) into the new Random
22 RandomGen(next, split)
25 , Random ( random, randomR,
35 import PrelGHC ( RealWorld )
36 import PrelShow ( showSignedInt, showSpace )
37 import PrelRead ( readDec )
38 import PrelIOBase ( unsafePerformIO, stToIO )
39 import PrelArr ( STRef, newSTRef, readSTRef, writeSTRef )
40 import PrelFloat ( float2Double, double2Float )
41 import Time ( getClockTime, ClockTime(..) )
43 import PrelPrim ( IORef
51 import CPUTime ( getCPUTime )
52 import Char ( isSpace, chr, ord )
56 class RandomGen g where
66 instance RandomGen StdGen where
71 instance Show StdGen where
72 showsPrec p (StdGen s1 s2) =
77 instance Show StdGen where
78 showsPrec p (StdGen s1 s2) =
84 instance Read StdGen where
88 _ -> [stdFromString r] -- because it shouldn't ever fail.
91 (s1, r1) <- readDec (dropWhile isSpace r)
92 (s2, r2) <- readDec (dropWhile isSpace r1)
93 return (StdGen s1 s2, r2)
96 If we cannot unravel the StdGen from a string, create
97 one based on the string given.
99 stdFromString :: String -> (StdGen, String)
100 stdFromString s = (mkStdGen num, rest)
101 where (cs, rest) = splitAt 6 s
102 num = foldl (\a x -> x + 3 * a) 1 (map ord cs)
106 mkStdGen :: Int -> StdGen -- why not Integer ?
108 | s < 0 = mkStdGen (-s)
109 | otherwise = StdGen (s1+1) (s2+1)
111 (q, s1) = s `divMod` 2147483562
112 s2 = q `mod` 2147483398
114 createStdGen :: Integer -> StdGen
116 | s < 0 = createStdGen (-s)
117 | otherwise = StdGen (fromInteger (s1+1)) (fromInteger (s2+1))
119 (q, s1) = s `divMod` 2147483562
120 s2 = q `mod` 2147483398
124 The class definition - see library report for details.
128 -- Minimal complete definition: random and randomR
129 random :: RandomGen g => g -> (a, g)
130 randomR :: RandomGen g => (a,a) -> g -> (a,g)
132 randoms :: RandomGen g => g -> [a]
133 randoms g = x : randoms g' where (x,g') = random g
135 randomRs :: RandomGen g => (a,a) -> g -> [a]
136 randomRs ival g = x : randomRs ival g' where (x,g') = randomR ival g
139 randomIO = getStdRandom random
141 randomRIO :: (a,a) -> IO a
142 randomRIO range = getStdRandom (randomR range)
146 instance Random Int where
147 randomR (a,b) g = randomIvalInteger (toInteger a, toInteger b) g
148 random g = randomR (minBound,maxBound) g
150 instance Random Char where
152 case (randomIvalInteger (toInteger (ord a), toInteger (ord b)) g) of
154 random g = randomR (minBound,maxBound) g
156 instance Random Bool where
158 case (randomIvalInteger (toInteger (bool2Int a), toInteger (bool2Int b)) g) of
159 (x, g) -> (int2Bool x, g)
167 random g = randomR (minBound,maxBound) g
169 instance Random Integer where
170 randomR ival g = randomIvalInteger ival g
171 random g = randomR (toInteger (minBound::Int), toInteger (maxBound::Int)) g
173 instance Random Double where
174 randomR ival g = randomIvalDouble ival id g
175 random g = randomR (0::Double,1) g
177 -- hah, so you thought you were saving cycles by using Float?
180 instance Random Float where
181 random g = randomIvalDouble (0::Double,1) realToFrac g
182 randomR (a,b) g = randomIvalDouble (realToFrac a, realToFrac b) realToFrac g
184 instance Random Float where
185 randomR (a,b) g = randomIvalDouble (float2Double a, float2Double b) double2Float g
186 random g = randomIvalDouble (0::Double,1) double2Float g
194 mkStdRNG :: Integer -> IO StdGen
197 return (createStdGen (ct + o))
199 mkStdRNG :: Integer -> IO StdGen
202 (TOD sec _) <- getClockTime
203 return (createStdGen (sec * 12345 + ct + o))
206 randomIvalInteger :: (RandomGen g, Num a) => (Integer, Integer) -> g -> (a, g)
207 randomIvalInteger (l,h) rng
208 | l > h = randomIvalInteger (h,l) rng
209 | otherwise = case (f n 1 rng) of (v, rng') -> (fromInteger (l + v `mod` k), rng')
220 f (n-1) (fromIntegral x + acc * b) g'
222 randomIvalDouble :: (RandomGen g, Fractional a) => (Double, Double) -> (Double -> a) -> g -> (a, g)
223 randomIvalDouble (l,h) fromDouble rng
224 | l > h = randomIvalDouble (h,l) fromDouble rng
226 case (randomIvalInteger (toInteger (minBound::Int), toInteger (maxBound::Int)) rng) of
230 fromDouble ((l+h)/2) +
231 fromDouble ((h-l) / realToFrac intRange) *
232 fromIntegral (x::Int)
237 intRange = toInteger (maxBound::Int) - toInteger (minBound::Int)
239 iLogBase :: Integer -> Integer -> Integer
240 iLogBase b i = if i < b then 1 else 1 + iLogBase b (i `div` b)
242 stdNext :: StdGen -> (Int, StdGen)
243 stdNext (StdGen s1 s2) = (z', StdGen s1'' s2'')
244 where z' = if z < 1 then z + 2147483562 else z
248 s1' = 40014 * (s1 - k * 53668) - k * 12211
249 s1'' = if s1' < 0 then s1' + 2147483563 else s1'
252 s2' = 40692 * (s2 - k' * 52774) - k' * 3791
253 s2'' = if s2' < 0 then s2' + 2147483399 else s2'
255 stdSplit :: StdGen -> (StdGen, StdGen)
256 stdSplit std@(StdGen s1 s2)
259 -- no statistical foundation for this!
260 left = StdGen new_s1 t2
261 right = StdGen t1 new_s2
263 new_s1 | s1 == 2147483562 = 1
266 new_s2 | s2 == 1 = 2147483398
269 StdGen t1 t2 = snd (next std)
276 setStdGen :: StdGen -> IO ()
277 setStdGen sgen = writeIORef theStdGen sgen
279 getStdGen :: IO StdGen
280 getStdGen = readIORef theStdGen
282 theStdGen :: IORef StdGen
283 theStdGen = unsafePerformIO (newIORef (createStdGen 0))
287 global_rng :: STRef RealWorld StdGen
288 global_rng = unsafePerformIO $ do
290 stToIO (newSTRef rng)
292 setStdGen :: StdGen -> IO ()
293 setStdGen sgen = stToIO (writeSTRef global_rng sgen)
295 getStdGen :: IO StdGen
296 getStdGen = stToIO (readSTRef global_rng)
301 newStdGen :: IO StdGen
304 let (a,b) = split rng
308 getStdRandom :: (StdGen -> (a,StdGen)) -> IO a
311 let (v, new_rng) = f rng