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