Introduction to Functional Programming – Random number generationTDA555 / DIT440, LP1 2015
Home | Schedule | Labs | Exercises | Exam | About | FAQFire | Forum | TimeEdit | Links
Introduction to Functional Programming – Random number generationTDA555 / DIT440, LP1 2015
Home | Schedule | Labs | Exercises | Exam | About | FAQFire | Forum | TimeEdit | Links

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

We begin by importing the module System.Random for random number generation:

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 (inclusive) interval which the result should lie within. The second argument StdGen is an abstract value used determine which random number to generate. The result is a random number and a new StdGen that can be used to generate new random numbers.

To test randomR 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 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 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)