Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Random number in Haskell [duplicate]

Tags:

random

haskell

I'm trying to get a random number in Haskell. (Which I'm currently learning and haven't got on to Monads or IO, etc) the problem is the functions in System.Random all return an IO Int, which I then can't use in the rest of my code which uses Int and Float.

The objective here is to choose a pair from a list where the first of the pair is a float representing a probability. So my plan was to use a random number to choose a pair based on its probability.

like image 655
Jonathan. Avatar asked Oct 25 '13 16:10

Jonathan.


3 Answers

This is a common barrier for new Haskell programmers. You want to escape IO, and it takes a while to figure out the best way to do so. The Learn You A Haskell tutorial has a good explanation of one way to generate random numbers using the state monad, but it still has to be seeded using getStdGen or newStdGen, which are in IO.

For simple cases you can just do something like

myPureFunction :: Float -> Float
myPureFunction x = 2 * x

main :: IO ()
main = do
    -- num :: Float
    num <- randomIO :: IO Float
    -- This "extracts" the float from IO Float and binds it to the name num
    print $ myPureFunction num

So you see, you can get your random number in main, then pass that value to a pure function that does the processing.

You may be asking yourself why there's all this work to generate random numbers in Haskell. There are numerous good reasons, most of which have to do with the type system. Since generating random numbers requires modifying the state of the StdGen in the operating system, it has to live inside IO, otherwise you could have a pure function that gives you different results each time.

Imagine this contrived scenario:

myConstant :: Int
myConstant = unsafePerformIO randomIO

blowUpTheWorld :: IO ()
blowUpTheWorld = error "Firing all the nukes"

main :: IO ()
main = do
    if even myConstant
        then print "myConstant is even"
        else blowUpTheWorld

If you ran this a few times, chances are that you would end up "firing all the nukes". Obviously, this is bad. myConstant should be, well, constant, but each time you run the program you'd get a different value. Haskell wants to guarantee that a pure function will always return the same value given the same inputs.

It may be annoying right now, but it's a powerful tool in the functional programmer's kit.

like image 101
bheklilr Avatar answered Nov 03 '22 16:11

bheklilr


There are good answers here, but I felt a more complete answer would show very simply how to get and use random numbers in Haskell, in a way that would make sense to imperative programmers.

First, you need a random seed:

import System.Random
newRand = randomIO :: IO Int

Because newRand is of type IO Int and not Int, it cannot be used as a function parameter. (This preserves Haskell functions as pure functions that will always return the same result on the same input.)

We can, however, simply enter newRand in GHCI and get a unique random seed each time. This is possible only because newRand is of type IO and is not a standard (immutable) variable or function.

*Main> newRand
-958036805781772734

We can then copy and paste this seed value into a function that creates a list of random numbers for us. If we define the following function:

randomList :: Int -> [Double]
randomList seed = randoms (mkStdGen seed) :: [Double]

And paste in the given seed when the function is run in GHCI:

*Main> take 10 randomList (-958036805781772734)
[0.3173710114340238,0.9038063995872138,0.26811089937893495,0.2091390866782773,0.6351036926797997,0.7343088946561198,0.7964520135357062,0.7536521528870826,0.4695927477527754,0.2940288797844678]

Notice how we get the familiar values from 0 to 1 (exclusive). Instead of generating a new random number each iteration like we would in an imperative language, we generate a list of random numbers ahead of time and use the head of the tail of the list on each successive recursion. An example:

pythagCheck :: [Double] -> [Double] -> [Int]
pythagCheck (x:xs) (y:ys)
   | (a^2) + (b^2) == (c^2) = [a, b, c]
   | otherwise              = pythagCheck xs ys
  where aplusb = ceiling (x * 666)
        a = ceiling (y * (fromIntegral (aplusb - 1)))
        b = aplusb - a
        c = 1000 - a - b

Creating two lists ahead of time and feeding them in as parameters allows us to search for the (one and only!) Pythagorean triple where a + b + c = 1000. You would, of course, want to use a different random seed for each list:

*Main> newRand
3869386208656114178
*Main> newRand
-5497233178519884041
*Main> list1 = randomList 3869386208656114178
*Main> list2 = randomList (-5497233178519884041)
*Main> pythagCheck list1 list2
[200,375,425]
like image 13
Matt Davis Avatar answered Nov 03 '22 15:11

Matt Davis


As already said, random numbers can't really be pure values1.

However, this doesn't really need to bother you. Just look at it the other way around: other languages simply don't have such a thing as pure values, it's always states with real-world interference you're dealing with. Haskell can do that as well, in the IO monad. You don't need to know how exactly that works, just imitate what it would look like in a procedural language (there are a few pitfalls here, though).

First of all you need some algorithm, that doesn't have anything to do with the language whatsoever. The obvious way is to accumulate the probabilities across the list, and use the resulting step-function as a map from [0, 1[ to your desired values.

probsListLookup :: [(Double, a)] -> Double -> a
probsListLookup pAssoc = look acc'dList
 where acc'dList = scanl1 (\(pa,_) (pn,x) -> (pa+pn,x)) pAssoc
       look ((pa, x) : pas) rval
         | rval < pa   = look pas rval
         | otherwise   = x

Note that this neither handles invalid inputs well (probabilities not summing to 1 etc.) nor is it efficient, scrambling O (n) through acc'dList for each requested value2. More important though, note that it's a pure function! It's generally a good idea to use pure functions as much as possible, and only go into IO when it's absolutely necessary. Like now: we need to obtain a single Double value between 0 and 1. Easy!

main = do
   lookupVal <- randomRIO (0, 1)
   print $ probsListLookup [(0.1, 1), (0.2, 2), (0.3, 4), (0.4, 5)] lookupVal

1At least not of a basic type like Int; you could actually do "pure computations" on whole probabilty distributions though. Doing that explicitly is very cumbersome, but Haskell allows you to use specific monads (or in fact comonads) to make that just as easy as it is in Haskell IO (or in any impure language) but without the dangers of input/output.

2You could improve that e.g. with Data.Map.

like image 7
leftaroundabout Avatar answered Nov 03 '22 15:11

leftaroundabout