2010-08-06 24 views
5

我是Haskell的新手,我想知道如何使此代碼更高效和整齊。似乎不必要的漫長和不整潔。改進代碼以生成分佈

我的腳本生成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 

任何幫助,將不勝感激,謝謝。

回答

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] 

(請注意,我用splitEvery從哈ndy split包,所以你必須安裝包,並添加import Data.List.Split (splitEvery)到您的進口。)

這種方法稍微更一般,我覺得有點整潔,但真正的主要區別只是我是使用randomssplitEvery

0

我不知道我理解你的代碼或你的問題......

但在我看來,所有你需要做的是隨機生成的0和1的列表,然後將每個的他們的長度爲map並將它們與foldl一起添加。

是這樣的:

makeListÑLIS =如果n/= 0那麼 makeList第(n-1)randomR(0,1):LIS 別的 LIS

然後使其應用地圖和摺疊或摺疊它。

+0

對不起,我沒有解釋得很厲害。我基本上試圖創建數據來建立標準的正態分佈。通過虛擬翻轉硬幣(結果1或-1)10次,並取結果的平均值,我們得到-0.2。通過這個過程說1000倍,我們可以繪製結果和他們的頻率,並獲得正態分佈。我想創建一個雙打列表,我可以繪製這個分佈圖。 – Ash 2010-08-06 23:20:53

+0

爲了澄清,該腳本的結果可以是[0.2,0.0,0.0,-0.4,0.6,0.0,-0.2,0.2,0.4,0.0] – Ash 2010-08-06 23:24:28

2

編輯:我發佈這個太快,並沒有匹配的行爲,現在應該是好的。

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

知識:如果你喜歡randoms然後使用MonadRandom - 它的岩石。

風格:只有導入符號有助於可讀性和有時可維護性。

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結束是像原來而是單子。在這種情況下,如果使用Data.List replicate函數,則應該熟悉replicateM

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

風格:如果我做了什麼,很多時候我想有一個實例在其自身的功能(的GetList),然後在一個單獨的功能重複。

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 

感謝您的幫助,我歡迎任何進一步的評論:)