Reputation: 1034
I just learned about random
function.
To my understanding, random
function takes a value of type which is an instance of RandomGen
and returns a random value whose value we can specify. On the other hand, mksdGen
takes an Int
and generates a random generator which random
function needs.
I was trying to generate random Bool
values. In order to do that, I made a function randomBool
.
randomBool :: Int -> Bool
randomBool = fst . random . mkStdGen
Then I found a lot more True
s than False
s with small numbers.
And I was curious about it and checked as following
> length $ takeWhile randomBool [1..]
53667
I think this means that for the first 53667 positive integers, random . mkStdGen
returns True
, which do not seem to be very random to me.
Is it very normal? Or am I doing something wrong that make True
happen more easily?
Upvotes: 12
Views: 2920
Reputation: 420
Computers are deterministic and can't generate random numbers. Rather, they rely on mathematical formulas that return a distribution of numbers that look random. These are called pseudo-random number generators. However, because of the determinism, we have the problem that if we ran these formulas the same way during each invocation of our program, we would get the same random number generators. Obviously, this is no good, since we want our numbers to be random! Thus, we have to provide the random generator an initial seed value that changes from run-to-run. For most people (i.e., those not doing cryptographical stuff), the random number generator is seeded by the current time. In Haskell, this pseudo-random generator is represented by the StdGen
type. The mkStdGen
function is used to create a random number generator with a seed. Unlike C, where there is one global random number generator, in Haskell, you can have as many as you like, and you can create them with different seeds.
However, there is a caveat: since the numbers are pseudo-random, there is no guarantee that random number generators created with different seeds return numbers that look random compared to the other. This means that when you call randomBool
and give it successive seed values, there is no guarantee that the number you get from the StdGen
you create is random compared to the StdGen
seeded with its successor. This is why you get almost 50000 True
's.
In order to get data that actually looks random, you need to continue using the same random number generator. If you notice, the random
Haskell function has a type StdGen -> (a, StdGen)
. Because Haskell is pure, the random
function takes a random number generator, generates a pseudo-random value (the first element of the return value) and then returns a new StdGen
which represents the generator seeded with the original seed, but ready to give a new random number. You need to keep this other StdGen
around and pass it to the next random
function in order to get random data.
Here is an example, generating three random bools, a
, b
, and c
.
randomBools :: StdGen -> (Bool, Bool, Bool)
randomBools gen = let (a, gen') = random gen
(b, gen'') = random gen''
(c, gen''') = random gen'''
in (a, b, c)
Notice how the gen
variable is "threaded" through the calls to random.
You can simplify passing state by using a state monad. For example,
import Control.Monad.State
import System.Random
type MyRandomMonad a = State StdGen a
myRandom :: Random a => MyRandomMonad a
myRandom = do
gen <- get -- Get the StdGen state from the monad
let (nextValue, gen') = random gen -- Generate the number, and keep the new StdGen
put gen' -- Update the StdGen in the monad so subsequent calls generate new random numbers
return nextValue
Now you can write the randomBools
function as:
randomBools' :: StdGen -> (Bool, Bool, Bool)
randomBools' gen = fst $ runState doGenerate gen
where doGenerate = do
a <- myRandom
b <- myRandom
c <- myRandom
return (a, b, c)
If you want to generate a (finite) list of Bool
s, you can do
randomBoolList :: StdGen -> Int -> ([Bool], StdGen)
randomBoolList gen length = runState (replicateM length myRandom) gen
Notice how we return the StdGen
as the second element of the returned pair, to allow it to be given to new functions.
More simply, if you just want to generate an infinite list of random values of the same type from an StdGen
, you can use the randoms
function. This has the signature (RandomGen g, Random a) => g -> [a]
. To generate an infinite list of Bool
using a starting seed of x
, you simply run randoms (mkStdGen x)
. You can implement your example using length $ takeWhile id (randoms (mkStdGen x))
. You should verify that you get different values for different initial values of x
, but always the same value if you supply the same x
.
Finally, if you don't care about being tied to the IO
monad, Haskell also provides a global random number generator, much like imperative languages. Calling the function randomIO
in the IO
monad will give you a random value of whatever type you like (as long as it is an instance of the Random
typeclass, at least). You can use this similarly to myRandom
above, except in the IO
monad. This has the added convenience that it is pre-seeded by the Haskell runtime, meaning you don't have to even worry about creating an StdGen
. So, to create a random list of 10 Bool
s in the IO
monad, all you have to do is replicateM 10 randomIO :: IO [Bool].
Hope this helps :)
Upvotes: 3
Reputation: 3273
Informally, when you call mkStdGen
with seeds that are close together you will get two 'similar' generators. In your example you're actually creating new generators for each seed supplied, and since those seeds are 1, 2, 3, etc., they'll yield similar streams.
When you call random
with a generator, you actually get back a new generator in the second element of the pair:
Prelude System.Random> random (mkStdGen 100) :: (Bool, StdGen)
(True,4041414 40692)
So a good idea is to use this provided generator for your next call to random
. I.e.,
Prelude System.Random> let (i, gen0) = random (mkStdGen 100) :: (Bool, StdGen)
Prelude System.Random> let (j, gen1) = random gen0 :: (Bool, StdGen)
Prelude System.Random> let (k, gen2) = random gen1 :: (Bool, StdGen)
Prelude System.Random> (i, j, k)
(True, False, False)
So to make a bunch of random values, you want to pass the generator as state. You can set this up manually via a State
monad or something, or just use the randoms
function, which handles passing the generator state for you:
Prelude System.Random> take 10 $ randoms (mkStdGen 100) :: [Bool]
[True,False,False,False,False,True,True,False,False,True]
If you don't particularly care about being in IO
(it happens) you can use randomIO
:
Prelude System.Random> import Control.Monad
Prelude System.Random Control.Monad> replicateM 10 randomIO :: IO [Bool]
[True,True,False,True,True,False,False,False,True,True]
This section of LYAH might be a useful read.
Upvotes: 16
Reputation: 6703
A random generator created by mkStdGen
doesn't necessarily generate a random value as its first result. To generate the next random number, use the random generator returned by the previous random
call.
For example, this code generates 10 Bool
s.
take 10 $ unfoldr (Just . random) (mkStdGen 1) :: [Bool]
Upvotes: 2