The source of the text is a literate Haskell file: RandomNumbers.lhs that you can download and load into GHC.




module RandomNumbers where

import System.Random

Random numbers are generated by the function

randomR :: (Int,Int) -> StdGen -> (Int,StdGen)

(Here, the type is specialized for Int, but it also works for many other types.)

The first argument is an interval which the result should lie within. The second argument StdGen is an abstract value used as to initialize the random number generation. The result is a random number and a new StdGen that can be used to generate new random numbers.

To restrandomR you first have to create an StdGen. Since StdGen is an abstract type, it can’t be created directly, but only through a helper function, such as:

mkStdGen :: Int -> StdGen

Now the user can make up an integer and use that to generate random numbers; e.g:

System.Random> fst (randomR (1,10) (mkStdGen 23456))
8
System.Random> fst (randomR (1,10) (mkStdGen 98765))
1

If you use the same interval and same StdGen many times, you will always get the same result:

System.Random> fst (randomR (1,10) (mkStdGen 23456))
8
System.Random> fst (randomR (1,10) (mkStdGen 23456))
8

(After all, randomR is a pure function.)

Now one might wonder what’s the point of all of this. We’re creating a random number from another random number that we had to make up ourselves. But it turns out that it is only for the first use of randomR that one has to create an StdGen. The result from randomR is a pair of a number and a new StdGen. By using the new StdGen to generate the next random number, it is possible to generate a sequence of independent random numbers.

Here is one way how to do that:

fourRandomNums :: StdGen -> (Int,Int,Int,Int)
fourRandomNums g0 = (a,b,c,d)
  where
    (a,g1) = randomR (1,10) g0
    (b,g2) = randomR (1,10) g1
    (c,g3) = randomR (1,10) g2
    (d,g4) = randomR (1,10) g3

Note how each randomR gets a differet StdGen. The above function generates four random numbers from a single StdGen (a “seed”):

*Main> fourRandomNums (mkStdGen 234523)
(4,6,10,1)

But as before, we get the same result for the same StdGen:

*Main> fourRandomNums (mkStdGen 234523)
(4,6,10,1)

If we want to avoid having to make up the seed ourselves, we can actually let the operating system do it for us, using newStdGen:

newStdGen :: IO StdGen

We can use newStdGen as follows:

testFourRandomNums :: IO ()
testFourRandomNums = do
    g <- newStdGen
    print (fourRandomNums g)

Now we can finally generate independent random numbers without having to worry about seeds:

*Main> testFourRandomNums
(3,2,7,9)
*Main> testFourRandomNums
(7,6,9,9)
*Main> testFourRandomNums
(2,6,9,5)

[Back to index]