2010-08-06 20 views
5

Soy nuevo en Haskell y me pregunto cómo y si puedo hacer que este código sea más eficiente y ordenado. Parece innecesariamente largo y desordenado.Mejorar el código para generar una distribución

Mi secuencia de comandos genera una lista de 10 promedios de 10 monedas.

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 

Cualquier ayuda sería apreciada, gracias.

Respuesta

3

Me gustaría abordar este problema de una manera ligeramente diferente. En primer lugar me gustaría definir una función que me daría un muestreo infinita de lanzamientos de una distribución de Bernoulli con una probabilidad de éxito p:

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

Entonces me gustaría escribir distBernoulli de la siguiente manera:

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 

pienso esto coincide con su definición de 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] 

(Tenga en cuenta que estoy usando splitEvery del ja ndy split paquete, por lo que tendría que instalar el paquete y agregar import Data.List.Split (splitEvery) a sus importaciones.)

Este enfoque es un poco más general, y creo que un poco más ordenado, pero la diferencia principal es que soy usando randoms y splitEvery.

0

No estoy seguro de entender su código o su pregunta ...

Pero parece que todo lo que tenía que hacer mi es generar una lista de los aleatorios y ceros, y luego dividir cada una de por su longitud con un map y agréguelos junto con un foldl.

Algo así como:

makeList n = lis si n/= 0, entonces makeList (n-1) randomR (0,1): lis demás lis

y luego hacer que aplique una Mapa y Foldl o Foldr a él.

+0

lo siento, yo lo expliqué mal. básicamente estoy tratando de crear los datos para construir la distribución normal estándar. virtualmente volteando una moneda (con el resultado 1 o -1) 10 veces, y tomando el promedio del resultado, obtenemos -0.2. al hacer este proceso digamos 1000 veces, podemos trazar los resultados y su frecuencia y obtener la distribución normal. Estoy tratando de crear una lista de dobles que puedo trazar para dibujar esta distribución. – Ash

+0

para aclarar, el resultado de este script podría ser [0.2,0.0,0.0, -0.4,0.6,0.0, -0.2,0.2,0.4,0.0] – Ash

2

EDIT: Publiqué esto demasiado rápido y no coincidía con el comportamiento, debería ser bueno ahora.

import Control.Monad.Random 
import Control.Monad (liftM, replicateM) 

CONOCIMIENTO: Si te gusta randoms a continuación, utilizar MonadRandom - que las rocas.

ESTILO: Solo la importación de símbolos que utilice ayuda a la legibilidad y, a veces, la capacidad de mantenimiento.

output :: IO [Double] 
output = liftM (map dist) getLists 

Nota: Me he dado salida a un tipo explícito, pero sabe que no tiene ser IO.

ESTILO:

1) Su generalmente bueno para separar el IO de funciones puras. Aquí he dividido la obtención de listas aleatorias del cálculo de distribuciones. En su caso, era puro, pero usted combinó la obtención de listas "aleatorias" a través de un generador con la función de distribución; Yo dividiría esas partes.

2) Lea Do notation considered harmful. Considere el uso de >>= en lugar de

output = do 
    gen <- new 
    return $ dist gen 

que puede hacer:

output = new >>= dist 

Wow!

dist :: [Int] -> Double 
dist lst = (fromIntegral (sum lst)/fromIntegral (length lst)) 

getLists :: MonadRandom m => Int -> Int -> m [[Int]] 
getLists m n= replicateM m (getList n) 

CONOCIMIENTO En Control.Monad cualquier cosa que termine en un M es igual que el original pero para mónadas. En este caso, replicateM debería ser familiar si utilizó la función Data.List replicate.

getList :: MonadRandom m => Int -> m [Int] 
getList m = liftM (map (subtract 1 . (*2)) . take m) (getRandomRs (0,1::Int)) 

ESTILO: Si hago algo muchas veces me gustaría tener un solo caso en su propia función (getList), entonces la repetición en una función separada.

0

Usando lo anterior, ahora estoy usando esto.

import Data.List 
import System.Random 

type Rand a = [a] 

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

listBernoulli :: StdGen -> Rand Int 
listBernoulli = map (\x -> (x*2)-1) . randomRs (0,1) 

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

Gracias por su ayuda, y la bienvenida a cualquier comentario adicional :)

Cuestiones relacionadas