# HG changeset patch
# User Bryan O'Sullivan
# Date 1253298937 0
# Node ID cfadd98cb7ae7c6dfdd6f2e7a32905a06ad31d40
# Parent a57979680c7d42596446f97329edc5bf99b02390
Improve documentation.
diff --git a/Statistics/RandomVariate.hs b/Statistics/RandomVariate.hs
--- a/Statistics/RandomVariate.hs
+++ b/Statistics/RandomVariate.hs
@@ -9,13 +9,6 @@
-- Portability : portable
--
-- Pseudo-random variate generation.
---
--- The uniform PRNG uses Marsaglia's MWC256 (also known as MWC8222)
--- multiply-with-carry generator, which has a period of 2^8222 and
--- fares well in tests of randomness.
---
--- /Note/: Marsaglia's PRNG is not known to be cryptographically
--- secure, so you should not use it for cryptographic operations.
module Statistics.RandomVariate
(
@@ -57,7 +50,16 @@
import System.IO (IOMode(..), hGetBuf, hPutStrLn, stderr, withBinaryFile)
import System.IO.Unsafe (unsafePerformIO)
--- | The class of types for which we can generate random variates.
+-- | The class of types for which we can generate uniformly
+-- distributed random variates.
+--
+-- The uniform PRNG uses Marsaglia's MWC256 (also known as MWC8222)
+-- multiply-with-carry generator, which has a period of 2^8222 and
+-- fares well in tests of randomness. It is extremely fast, between 2
+-- and 3 times faster than the Mersenne Twister.
+--
+-- /Note/: Marsaglia's PRNG is not known to be cryptographically
+-- secure, so you should not use it for cryptographic operations.
class Variate a where
-- | Generate a single uniformly distributed random variate. The
-- range of values produced varies by type:
@@ -204,8 +206,9 @@
--
-- > initialize (toU [4, 8, 15, 16, 23, 42])
--
--- /Note/: supplying a seed containing fewer than 256 elements may
--- lead to less-than-desirable randomness.
+-- If a seed contains fewer than 256 elements, it is first used
+-- verbatim, then its elements are 'xor'ed against elements of the
+-- default seed until 256 elements are reached.
initialize :: UArr Word32 -> ST s (Gen s)
initialize seed = do
q <- newMU 258
@@ -215,9 +218,12 @@
return (Gen q)
where fill q = go 0 where
go i | i == 256 = return ()
- | otherwise = writeMU q i (indexU s i) >> go (i+1)
- where s | i >= fini = defaultSeed
- | otherwise = seed
+ | otherwise = writeMU q i s >> go (i+1)
+ where s | i >= fini = if fini == 0
+ then indexU defaultSeed i
+ else indexU defaultSeed i `xor`
+ indexU seed (i `mod` fini)
+ | otherwise = indexU seed i
fini = lengthU seed
{-# INLINE initialize #-}
@@ -229,26 +235,24 @@
c <- (numerator . (%cpuTimePrecision)) `fmap` getCPUTime
t <- toRational `fmap` getPOSIXTime
let n = fromIntegral (numerator t) :: Word64
- l = fromIntegral n
- h = fromIntegral (n `shiftR` 32)
- seed = zipWithU xor defaultSeed . toU . take 256 . cycle $
- [fromIntegral c,l,h]
- return . runST $ initialize seed >>= act
+ seed = [fromIntegral c, fromIntegral n, fromIntegral (n `shiftR` 32)]
+ return . runST $ initialize (toU seed) >>= act
-- | Seed a PRNG with data from the system's fast source of
--- pseudo-random numbers (\"/dev/urandom\" on Unix-like systems), then
--- run the given action.
+-- pseudo-random numbers (\"\/dev\/urandom\" on Unix-like systems),
+-- then run the given action.
--
--- /Note/: on Windows, this code does not yet use the Windows
--- Cryptographic API as a source of random numbers, and generates much
--- poorer sequences.
+-- /Note/: on Windows, this code does not yet use the native
+-- Cryptographic API as a source of random numbers (it uses the system
+-- clock instead). As a result, the sequences it generates may not be
+-- highly independent.
withSystemRandom :: (forall s. Gen s -> ST s a) -> IO a
withSystemRandom act = tryRandom `catch` \(_::IOException) -> do
seen <- atomicModifyIORef warned ((,) True)
unless seen $ do
hPutStrLn stderr ("Warning: Couldn't open " ++ show random)
hPutStrLn stderr ("Warning: using system clock for seed instead " ++
- "(quality is much lower)")
+ "(quality will be lower)")
withTime act
where tryRandom = do
let nbytes = 1024
@@ -330,7 +334,8 @@
--
-- The implementation uses Doornik's modified ziggurat algorithm.
-- Compared to the ziggurat algorithm usually used, this is slower,
--- but generates higher-quality numbers that pass tests of randomness.
+-- but generates more independent variates that pass stringent tests
+-- of randomness.
normal :: Gen s -> ST s Double
normal gen = loop
where
@@ -429,3 +434,8 @@
-- * Marsaglia, G. (2003) Seeds for random number generators.
-- /Communications of the ACM/ 46(5):90–93.
--
+--
+-- * Thomas, D.B.; Leong, P.G.W.; Luk, W.; Villasenor, J.D.
+-- (2007). Gaussian random number generators.
+-- /ACM Computing Surveys/ 39(4).
+--
diff --git a/tests/T.hs b/tests/T.hs
--- a/tests/T.hs
+++ b/tests/T.hs
@@ -18,23 +18,23 @@
time act = do
s <- getTime
ret <- act
- e <- getTime
+ e <- ret `seq` getTime
return (e-s, ret)
-count = 50000000
+count = floor 1e8
-type T = Word64
+type T = Word32
--summ :: [T] -> T
--summ = foldl' (+) 0
-loop :: Monad m => T -> m T -> m T
+loop :: Monad m => Int -> m T -> m T
loop n act = go 0 0
where
go !i !s | i >= n = return s
| otherwise = do
d <- act
- go (i+1) (s+fromIntegral d)
+ go (i+1) (s+d)
mwc :: Word32 -> IO T
mwc k = return $! runST go where
@@ -42,6 +42,12 @@
gen <- initialize (singletonU k)
loop count (uniform gen)
+mwca :: Word32 -> IO T
+mwca k = return $! runST go where
+ go = do
+ gen <- initialize (singletonU k)
+ sumU `fmap` uniformArray gen count
+
mersenne :: IO T
mersenne = do
gen <- getStdGen
@@ -51,5 +57,7 @@
forM_ [1..3] $ \n -> do
putStr "mwc: "
print =<< time (mwc n)
+ putStr "mwca: "
+ print =<< time (mwca n)
putStr "mersenne: "
print =<< time mersenne