简体   繁体   中英

Random number sequence un Haskell and State Monad, what am I doing wrong?

As part of my Haskell journey, I am implementing a raytracer and I need to be able to draw sequences of random numbers at several places in the code. Typically I would like to be able to get say 64 samples for each pixels and pixels are computed in parallel.

I was looking at the state monad to achieve that and I was guided by this answer Sampling sequences of random numbers in Haskell but the code I wrote does not terminate and it's memory consumption explodes.

Here is the abstracted part of the code: I was hopping to be able to call sampleUniform several time in the code to get new lists of random numbers but if I do runhaskell test.hs , it outputs the first character of the lis [ and then it is stuck in an apparently infinite loop.

module Main (main
            , computeArray) where

import Control.Monad
import Control.Monad.State (State, evalState, get, put)
import System.Random (StdGen, mkStdGen, random)
import Control.Applicative ((<$>))

type Rnd a = State StdGen a

runRandom :: Rnd a -> Int -> a
runRandom action seed = evalState action $ mkStdGen seed

rand :: Rnd Double
rand = do
  gen <- get
  let (r, gen') = random gen
  put gen'
  return r

{- Uniform distributions -}
uniform01 :: Rnd [Double]
uniform01 = mapM (\_ -> rand) $ repeat ()

{- Get n samples uniformly distributed between 0 and 1 -}
sampleUniform :: Int -> Rnd [Double]
sampleUniform n = liftM (take n) uniform01

computeArray :: Rnd [Bool]
computeArray = do
  samples1 <- sampleUniform 10
  samples2 <- sampleUniform 10
  let dat = zip samples1 samples2
  return $ uncurry (<) <$> dat

main :: IO ()
main = do
  let seed = 48
  let res = runRandom computeArray seed
  putStrLn $ show res

uniform01 threads your state through an infinite number of computations, which means that although it produces its result lazily, there is no hope of retrieving a final state at the end to use for the next sampling. liftM (take n) only affects the final value, not the state effects used to compute it. Therefore as written, you can only use uniform01 / sampleUniform once.

Instead you can thread the state through only as many rand actions as you use, eg with

sampleUniform n = mapM (\_ -> rand) $ replicate n ()

or simpler

sampleUniform n = sequence $ replicate n rand

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM