Introduction to Functional Programming – Random number generation | TDA555 / DIT440, LP1 2014 |
Home | Schedule | Labs | Exercises | Exam | About | FAQ | Fire | Forum | TimeEdit | Links |
Introduction to Functional Programming – Random number generation | TDA555 / DIT440, LP1 2014 |
Home | Schedule | Labs | Exercises | Exam | About | FAQ | Fire | 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 declaring a module name and importing the module System.Random
for random number generation:
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 (inclusive) 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 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 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)