我想在Haskell中獲得一個隨機數。 (我目前正在學習,還沒有上Monads或IO等)問題是System.Random中的函數都返回一個IO Int,然後我不能在我的代碼的其餘部分使用它Int和Float。Haskell中的隨機數
這裏的目標是從列表中選擇一對,其中第一對是表示概率的浮點數。所以我的計劃是使用一個隨機數來根據它的概率來選擇一對。
我想在Haskell中獲得一個隨機數。 (我目前正在學習,還沒有上Monads或IO等)問題是System.Random中的函數都返回一個IO Int,然後我不能在我的代碼的其餘部分使用它Int和Float。Haskell中的隨機數
這裏的目標是從列表中選擇一對,其中第一對是表示概率的浮點數。所以我的計劃是使用一個隨機數來根據它的概率來選擇一對。
如果你想要真正的隨機性,你不會使用IO--聽起來像是一個拖拽,但是這種分離是Haskell的一個非常重要的方面。但是,您可以通過自己選擇「種子」並使用System.Random中返回一對結果和新種子(例如「random」)的純函數來獲得半僞隨機性。
這是新Haskell程序員的共同障礙。你想要逃脫IO,並且需要一段時間才能找出最佳的方式。學習你的Haskell教程對使用狀態monad生成隨機數字的一種方法有很好的解釋,但是它仍然必須使用getStdGen
或newStdGen
來進行播種,它們在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希望保證在給定相同輸入的情況下,純函數總是會返回相同的值。
它現在可能很煩人,但它是功能程序員套件中的一個強大工具。
如前所述,隨機數不能是純粹的值。
但是,這並不真的需要打擾你。反過來看看它:其他語言根本就沒有純粹價值這樣的東西,它總是與你正在處理的現實世界的干擾狀態。 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
。
我不認爲這些答案是全貌。對於我的模擬,我會懶惰地生成隨機數並嚴格按照小型(我的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
這裏有很好的答案,但我覺得一個更完整的答案會表現的很乾脆如何獲得和Haskell中使用隨機數,在某種程度上將是有意義的勢在必行的程序員。
首先,你需要一個隨機種子:
import System.Random
newRand = randomIO :: IO Int
因爲newRand
是IO 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]
隨機數字是不純的,這就是它們被包裝在'IO'單元中的原因。你到現在爲止嘗試過什麼?請給我們一些代碼。 –
本頁底部有一個很好的簡單示例http://hackage.haskell.org/package/MonadRandom-0.1.12/docs/Control-Monad-Random.html – mhwombat
使用Numeric.GSL.Distribution:https:/ /hackage.haskell.org/package/hmatrix-gsl-stats –