Introduction to Functional Programming – Random number generationTDA555 / DIT440, LP1 2016
Home | Schedule | Labs | Lectures | Exercises | Exam | About | FAQFire | Forum | TimeEdit | YouTube | Links
Introduction to Functional Programming – Random number generationTDA555 / DIT440, LP1 2016
Home | Schedule | Labs | Lectures | Exercises | Exam | About | FAQFire | Forum | TimeEdit | YouTube | 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)

This is all you should need for Lab 2, but not the end of the story. What if you want to create a StdGen from a randomly chosen seed? This is a chicken-and-egg problem! Fortunately there is an IO instruction that when executed will fetch a seed from the operating system (which always has a way to generate random values), and use it to create a StdGen:

newStdGen :: IO StdGen

We can use newStdGen as follows (but you need to first understand IO before this will make sense!):

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)