改进代码以生成分发

时间:2010-08-06 22:42:21

标签: haskell statistics

我是Haskell的新手,我想知道如何使这段代码变得更加高效和整洁。这看起来不必要长而不整洁。

我的脚本生成10个平均10个硬币翻转的列表。

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

任何帮助都将不胜感激,谢谢。

4 个答案:

答案 0 :(得分:3)

我会以稍微不同的方式解决这个问题。首先,我将定义一个函数,该函数可以从伯努利分布中获得无限的翻转样本,成功概率为p

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

然后我按如下方式写distBernoulli

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

我认为这符合您对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]

(请注意,我在方便的split包中使用splitEvery,因此您必须安装该软件包并将import Data.List.Split (splitEvery)添加到您的导入中。)

这种方法略胜一筹,我觉得有点整洁,但主要区别在于我正在使用randomssplitEvery

答案 1 :(得分:2)

编辑:我发布的内容太快而且行为不匹配,现在应该很好。

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

知识:如果你喜欢randoms然后使用MonadRandom - 它会摇滚。

STYLE:只导入您使用的符号有助于提高可读性和可维护性。

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

注意:我给输出一个显式类型,但知道它 不是IO。

型:

1)将IO与纯函数分开通常很好。在这里,我分析了从分布计算中获取随机列表。在你的情况下它是纯粹的,但你结合通过发电机获得“随机”列表与分配功能;我会将这些部分分开。

2)阅读Do notation considered harmful。请考虑使用>>=代替

output = do
   gen <- new
   return $ dist gen

你可以这样做:

output = new >>= dist

哇!

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)

知识在Control.Monad中以M结尾的任何内容都与原始内容相似,但对于monad而言。在这种情况下,如果您使用Data.List replicateM函数,replicate应该很熟悉。

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

STYLE:如果我多次做某事,我喜欢在自己的函数(getList)中有一个实例,然后在一个单独的函数中重复。

答案 2 :(得分:0)

我不确定我理解您的代码或您的问题......

但在我看来,你需要做的就是生成一个随机的1和0的列表,然后用map将它们的长度除以它们,并将它们与{{1}一起添加}}

类似的东西:

makeList n lis = if n / = 0 makeList(n-1)randomR(0,1):lis 其他 LIS

然后让它应用Map和Foldl或Foldr。

答案 3 :(得分:0)

使用上述内容,我现在正在使用它。

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

感谢您的帮助,我欢迎任何进一步的评论:)