2013-10-25 35 views
15

我想在Haskell中獲得一個隨機數。 (我目前正在學習,還沒有上Monads或IO等)問題是System.Random中的函數都返回一個IO Int,然後我不能在我的代碼的其餘部分使用它Int和Float。Haskell中的隨機數

這裏的目標是從列表中選擇一對,其中第一對是表示概率的浮點數。所以我的計劃是使用一個隨機數來根據它的概率來選擇一對。

+2

隨機數字是不純的,這就是它們被包裝在'IO'單元中的原因。你到現在爲止嘗試過什麼?請給我們一些代碼。 –

+2

本頁底部有一個很好的簡單示例http://hackage.haskell.org/package/MonadRandom-0.1.12/docs/Control-Monad-Random.html – mhwombat

+0

使用Numeric.GSL.Distribution:https:/ /hackage.haskell.org/package/hmatrix-gsl-stats –

回答

1

如果你想要真正的隨機性,你不會使用IO--聽起來像是一個拖拽,但是這種分離是Haskell的一個非常重要的方面。但是,您可以通過自己選擇「種子」並使用System.Random中返回一對結果和新種子(例如「random」)的純函數來獲得半僞隨機性。

21

這是新Haskell程序員的共同障礙。你想要逃脫IO,並且需要一段時間才能找出最佳的方式。學習你的Haskell教程對使用狀態monad生成隨機數字的一種方法有很好的解釋,但是它仍然必須使用getStdGennewStdGen來進行播種,它們在IO中。

對於簡單的情況下,你可以做類似

myPureFunction :: Float -> Float 
myPureFunction x = 2 * x 

main :: IO() 
main = do 
    -- num :: Float 
    num <- randomIO :: IO Float 
    -- This "extracts" the float from IO Float and binds it to the name num 
    print $ myPureFunction num 

所以你看,你可以在你的main隨機數,則該值傳遞給一個純函數,它的處理。

您可能會問自己爲什麼這些工作都是爲了在Haskell中生成隨機數。有許多很好的理由,其中大部分與類型系統有關。由於生成隨機數字需要修改操作系統中StdGen的狀態,因此它必須存在於IO之內,否則您可能擁有一個純函數,每次都會給您不同的結果。

想象一下這樣的情景做作:

myConstant :: Int 
myConstant = unsafePerformIO randomIO 

blowUpTheWorld :: IO() 
blowUpTheWorld = error "Firing all the nukes" 

main :: IO() 
main = do 
    if even myConstant 
     then print "myConstant is even" 
     else blowUpTheWorld 

如果你跑了幾次,有機會,你會落得「發射所有的核武器」。顯然,這很糟糕。 myConstant應該是恆定的,但每次運行該程序時都會得到不同的值。 Haskell希望保證在給定相同輸入的情況下,純函數總是會返回相同的值。

它現在可能很煩人,但它是功能程序員套件中的一個強大工具。

4

如前所述,隨機數不能是純粹的值。

但是,這並不真的需要打擾你。反過來看看它:其他語言根本就沒有純粹價值這樣的東西,它總是與你正在處理的現實世界的干擾狀態。 Haskell也可以在IO monad中做到這一點。你不需要知道它是如何工作的,只是模仿程序語言的樣子(儘管這裏有一些缺陷)。

首先你需要一些算法,這與語言沒有任何關係。顯而易見的方法是在整個列表中累積概率,並將得到的階梯函數用作從[0,1]到所需值的映射。

probsListLookup :: [(Double, a)] -> Double -> a 
probsListLookup pAssoc = look acc'dList 
where acc'dList = scanl1 (\(pa,_) (pn,x) -> (pa+pn,x)) pAssoc 
     look ((pa, x) : pas) rval 
     | rval < pa = look pas rval 
     | otherwise = x 

注意,這既不處理無效的輸入孔(未概率總和爲1等)也不是有效的,對於每個請求值擾øÑ)通過acc'dList。更重要的是,請注意,這是一個純功能!儘可能地使用純函數通常是一個好主意,只有在絕對必要時纔會進入IO。像現在一樣:我們需要獲得0到1之間的單個Double值。簡單!

main = do 
    lookupVal <- randomRIO (0, 1) 
    print $ probsListLookup [(0.1, 1), (0.2, 2), (0.3, 4), (0.4, 5)] lookupVal 

至少沒有基本的類型等Int的;你可能實際上做整個概率分佈雖然「純粹計算」。明確這樣做非常麻煩,但Haskell允許您使用specific monad(或in fact comonads)使其與Haskell IO(或任何不純的語言)一樣容易,但沒有輸入/輸出的危險。

你可以改善例如,與Data.Map

2

我不認爲這些答案是全貌。對於我的模擬,我會懶惰地生成隨機數並嚴格按照小型(我的macbook上的1.1M)空間佔用空間運行它們。

也許關於隨機數只能存在於IO monad中的說法指的是真正的隨機數,但對於僞隨機數來說並非如此,並且通常希望能夠重現結果。這裏有一個例子:

module Main (
    main 
) where 

import qualified Data.Vector.Unboxed as V 
import Data.Random.Source.PureMT 
import Data.Random 
import Control.Monad.State 

nItt :: Int 
nItt = 1000000000 

gridSize :: Int 
gridSize = 10 

testData :: Int -> V.Vector Double 
testData m = 
    V.fromList $ 
    evalState (replicateM m (sample (uniform (0 :: Double) 1.0))) 
    (pureMT 2) 

test = V.foldl (+) 0 (testData nItt) 

main = putStrLn $ show test 
6

這裏有很好的答案,但我覺得一個更完整的答案會表現的很乾脆如何獲得和Haskell中使用隨機數,在某種程度上將是有意義的勢在必行的程序員。

首先,你需要一個隨機種子:

import System.Random 
newRand = randomIO :: IO Int 

因爲newRandIO Int型的,而不是Int,它不能被用作函數參數。 (這保留了Haskell函數作爲純函數,將始終在相同的輸入上返回相同的結果。)

但是,我們可以簡單地在GHCI中輸入newRand並每次獲得唯一的隨機種子。這可能只是因爲newRand的類型爲IO而不是標準(不可變)變量或函數。

*Main> newRand 
-958036805781772734 

然後,我們可以將此種子值複製並粘貼到一個函數中,爲我們創建一個隨機數列表。如果我們定義瞭如下功能:

randomList :: Int -> [Double] 
randomList seed = randoms (mkStdGen seed) :: [Double] 

而且在給定的種子粘貼時運行該功能在GHCI:

*Main> take 10 randomList (-958036805781772734) 
[0.3173710114340238,0.9038063995872138,0.26811089937893495,0.2091390866782773,0.6351036926797997,0.7343088946561198,0.7964520135357062,0.7536521528870826,0.4695927477527754,0.2940288797844678] 

通知我們如何熟悉的值從0到1(不包括)。我們不必每次迭代都會產生一個新的隨機數,而是像命令式語言一樣,我們會提前生成一個隨機數列表,並在每個連續遞歸中使用列表尾部的頭部。舉個例子:

pythagCheck :: [Double] -> [Double] -> [Int] 
pythagCheck (x:xs) (y:ys) 
    | (a^2) + (b^2) == (c^2) = [a, b, c] 
    | otherwise    = pythagCheck xs ys 
    where aplusb = ceiling (x * 666) 
     a = ceiling (y * (fromIntegral (aplusb - 1))) 
     b = aplusb - a 
     c = 1000 - a - b 

創建兩個列表的時間提前,並在餵養它們作爲參數允許我們搜索畢達哥拉斯三重其中a + B + C = 1000。你會的,當然(唯一!) ,想要爲每個列表使用不同的隨機種子:

*Main> newRand 
3869386208656114178 
*Main> newRand 
-5497233178519884041 
*Main> list1 = randomList 3869386208656114178 
*Main> list2 = randomList (-5497233178519884041) 
*Main> pythagCheck list1 list2 
[200,375,425]