Pseudo-random number generation using 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 also
extremely fast, between 2 and 3 times faster than the Mersenne
Twister. There are two representation of generator: Gen which is
generator that uses in-place mutation and Seed which is immutable
snapshot of generator's state.
Initialization
Generator could be initialized in several ways. One is to obtain
randomness from operating system using createSystemRandom,
createSystemSeed or withSystemRandomST (All examples assume
that System.Random.Stateful is imported)
Example2 expressions
>>> g <- createSystemRandom>>> uniformM g :: IO Int...
Example1 expression
>>> withSystemRandomST $ \g -> uniformM g :: IO Int...
Deterministically create generator from given seed using
initialize function:
Example5 expressions
>>> import Data.Int>>> import qualified Data.Vector.Unboxed as U>>> import System.Random.Stateful>>> g <- initialize $ U.fromList [1,2,3]>>> uniformRM (1,200) g :: IO Int64101
Last way is to create generator with fixed seed which could be
useful in testing
Example2 expressions
>>> g <- create>>> uniformM g :: IO Int-8765701622605876598
Generation of random numbers
Recommended way of generating random numbers in simple cases like
generating uniformly distributed random number in range or value
uniformly distributed in complete type domain is to use
UniformRange and Uniform type classes. Note that while small
self-contained examples usually require explicit annotations
usually result type could be inferred.
This example simulates 20 throws of fair 6-sided dice:
For generating full range of possible values one could use
uniformM. This example generates 10 random bytes, or equivalently
10 throws of 256-sided dice:
Example2 expressions
>>> g <- create>>> replicateM 10 $ uniformM g :: IO [Word8][209,138,126,150,165,15,69,203,155,146]
There're special cases for generating random vectors and
bytestrings. For example in order to generate random 10-byte
sequences as unboxed vector or bytestring:
Example2 expressions
>>> g <- create>>> uniformVector g 10 :: IO (U.Vector Word8)[209,138,126,150,165,15,69,203,155,146]
Example3 expressions
>>> import qualified Data.ByteString as BS>>> g <- create>>> BS.unpack <$> uniformByteStringM 10 g[138,242,130,33,209,248,89,134,150,180]
For repeatability, the state of the generator can be snapshotted
and replayed using the save and restore functions. Following
example shows how to save and restore generator:
Example5 expressions
>>> g <- create>>> replicateM_ 10 (uniformM g :: IO Word64)>>> s <- save g>>> uniformM g :: IO Word321771812561>>> uniformM =<< restore s :: IO Word321771812561
Create a generator for variates using the given seed, of which up
to 256 elements will be used. For arrays of less than 256
elements, part of the default seed will be used to finish
initializing the generator's state.
Examples:
initialize (singleton 42)
initialize (fromList [4, 8, 15, 16, 23, 42])
If a seed contains fewer than 256 elements, it is first used
verbatim, then its elements are xored against elements of the
default seed until 256 elements are reached.
If a seed contains exactly 258 elements, then the last two elements
are used to set the generator's initial state. This allows for
complete generator reproducibility, so that e.g. gen' == gen in
the following example:
In the MWC algorithm, the carry value must be strictly smaller than the
multiplicator (see https://en.wikipedia.org/wiki/Multiply-with-carry).
Hence, if a seed contains exactly 258 elements, the carry value, which is
the last of the 258 values, is moduloed by the multiplicator.
Note that if the first carry value is strictly smaller than the multiplicator,
all subsequent carry values are also strictly smaller than the multiplicator
(a proof of this is in the comments of the code of uniformWord32), hence
when restoring a saved state, we have the guarantee that moduloing the saved
carry won't modify its value.
Seed PRNG with data from the system's fast source of
pseudo-random numbers and execute computation in ST monad.
Type helpers
The functions in this package are deliberately written for
flexibility, and will run in both the IO and ST monads.
This can defeat the compiler's ability to infer a principal type in
simple (and common) cases. For instance, we would like the
following to work cleanly:
import System.Random.MWC
import Data.Vector.Unboxed
main = do
v <- withSystemRandom $ \gen -> uniformVector gen 20
print (v :: Vector Int)
Unfortunately, the compiler cannot tell what monad uniformVector
should execute in. The "fix" of adding explicit type annotations
is not pretty:
{-# LANGUAGE ScopedTypeVariables #-}
import Control.Monad.ST
main = do
vs <- withSystemRandom $
\(gen::GenST s) -> uniformVector gen 20 :: ST s (Vector Int)
print vs
As a more readable alternative, this library provides asGenST and
asGenIO to constrain the types appropriately. We can get rid of
the explicit type annotations as follows:
main = do
vs <- withSystemRandom . asGenST $ \gen -> uniformVector gen 20
print (vs :: Vector Int)
This is almost as compact as the original code that the compiler
rejected.
NOTE: Consider use of more principled type classes
Uniform and UniformRange instead.
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 also 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.
Generate a single uniformly distributed random variate. The
range of values produced varies by type:
For fixed-width integral types, the type's entire range is
used.
For floating point numbers, the range (0,1] is used. Zero is
explicitly excluded, to allow variates to be used in
statistical calculations that require non-zero values
(e.g. uses of the log function).
To generate a Float variate with a range of [0,1), subtract
2**(-33). To do the same with Double variates, subtract
2**(-53).
Generate a vector of pseudo-random variates. This is not
necessarily faster than invoking uniform repeatedly in a loop,
but it may be more convenient to use in some situations.
Convert vector to Seed. It acts similarly to initialize and
will accept any vector. If you want to pass seed immediately to
restore you better call initialize directly since following law holds: