Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Improving code to generate a distribution

I am new to Haskell and I wonder how/if I can make this code more efficient and tidy. It seems unnecessarily long and untidy.

My script generates a list of 10 averages of 10 coin flips.

import Data.List
import System.Random

type Rand a = StdGen -> Maybe (a,StdGen)

output = do
    gen <- newStdGen
    return $ distBernoulli 10 10 gen

distBernoulli :: Int -> Int -> StdGen -> [Double]
distBernoulli m n gen = [fromIntegral (sum x) / fromIntegral (length x) | x <- lst]
    where lst = splitList (randomList (n*m) gen) n

splitList :: [Int] -> Int -> [[Int]]
splitList [] n = []
splitList lst n = take n lst : splitList (drop n lst) n

randomList :: Int -> StdGen -> [Int]
randomList n = take n . unfoldr trialBernoulli

trialBernoulli :: Rand Int
trialBernoulli gen = Just ((2*x)-1,y)
                 where (x,y) = randomR (0,1) gen

Any help would be appreciated, thanks.

like image 249
Ash Avatar asked Aug 06 '10 22:08

Ash


1 Answers

I'd tackle this problem in a slightly different way. First I'd define a function that would give me an infinite sampling of flips from a Bernoulli distribution with success probability p:

flips :: Double -> StdGen -> [Bool]
flips p = map (< p) . randoms

Then I'd write distBernoulli as follows:

distBernoulli :: Int -> Int -> StdGen -> [Double]
distBernoulli m n = take m . map avg . splitEvery n . map val . flips 0.5
  where
    val True = 1
    val False = -1
    avg = (/ fromIntegral n) . sum

I think this matches your definition of distBernoulli:

*Main> distBernoulli 10 10 $ mkStdGen 0
[-0.2,0.4,0.4,0.0,0.0,0.2,0.0,0.6,0.2,0.0]

(Note that I'm using splitEvery from the handy split package, so you'd have to install the package and add import Data.List.Split (splitEvery) to your imports.)

This approach is slightly more general, and I think a little neater, but really the main difference is just that I'm using randoms and splitEvery.

like image 182
Travis Brown Avatar answered Sep 25 '22 19:09

Travis Brown