2013-10-25 2 views
15

하스켈에서 임의의 숫자를 얻으려고합니다. (어느 내가 현재 배우고 있고 Monads 또는 입출력, 등)에있어 문제가 System.Random에있는 모든 IO Int를 반환합니다, 그럼 내가 사용하는 내 코드의 나머지 부분에서 사용할 수 없습니다 Int 및 Float.하스켈에서 임의의 숫자

여기의 목적은 쌍 중 첫 번째가 확률을 나타내는 float 인 목록에서 한 쌍을 선택하는 것입니다. 그래서 내 계획은 확률에 따라 한 쌍을 선택하기 위해 난수를 사용하는 것이 었습니다.

+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 사운드를 사용하지 않아도되지만이 분리는 하스켈의 중요한 측면입니다. 그러나 자신이 "시드"를 선택하고 결과와 새 시드 (예 : "임의")를 반환하는 System.Random의 순수 함수를 사용하여 준 의사 난수를 얻을 수 있습니다.

21

이것은 새로운 Haskell 프로그래머에게 공통적 인 장벽입니다. 은 IO를 피하기 위해을 원합니다. 이렇게하는 것이 가장 좋은 방법입니다. Learn You Haskell 튜토리얼은 상태 모나드를 사용하여 난수를 생성하는 한 가지 방법에 대한 좋은 설명이 있지만 여전히 입방에있는 또는 newStdGen을 사용하여 시드해야합니다.

간단한 경우를 들어 방금

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에 임의의 숫자를 얻을 수 있습니다 볼과 같은 작업을 수행 할 수 있습니다, 다음 처리를 수행하는 순수한 함수에 그 값을 전달합니다.

하스켈에서 난수를 생성하는 모든 작업이 왜 필요한지 물어볼 수 있습니다. 수많은 좋은 이유가 있으며, 그 중 대부분은 유형 시스템과 관련이 있습니다. 난수 생성은 운영 체제에서 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 모나드에서 그렇게 할 수 있습니다. 그 코드가 어떻게 작동 하는지를 알 필요가 없으며 절차 적 언어로 보일 것만 흉내낼 수 있습니다 (단 몇 가지 함정이 있습니다).

우선 무엇보다 언어와 관련이없는 알고리즘이 필요합니다. 확실한 방법은리스트 전체에 걸쳐 확률을 누적하는 것이며 결과 단계 함수를 [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 

참고로이 둘 각각 요청한 값 2위한 acc'dList 통해 O (N) 스크램블링, 잘못된 입력이 잘 (확률 1 개 등을 합산하지) 않고는 효율적으로 처리한다. 더 중요한데, 그것은 순수 함수입니다. 가능한 한 순수 함수를 사용하는 것이 일반적으로 좋은 생각이며 절대적으로 필요한 경우에만 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 

1 적어도 Int 같은 기본 타입; 당신 은 실제로 확률 분포 전체에서 "순수 계산"을 할 수 있습니다. 명시 적으로 처리하는 것은 매우 부담 스럽지만 하스켈에서는 specific monad (또는 in fact comonads)을 사용하여 하스켈 IO (또는 불순한 언어) 에서처럼 쉽게 입력/출력의 위험성을 없앨 수 있습니다.

Data.Map.

2

나는이 대답이 전체 그림이라고 생각하지 않습니다. 내 시뮬레이션을 위해 나는 난수를 느리게 생성하고 엄밀히 말하면 (MacBook의 경우 1.1M) 공간 풋 프린트를 사용합니다.

아마도 IO 모나드에 난수가있을 수 있다는 말은 실제로 임의의 수를 나타냅니다. 그러나 사실이 아닌 의사 난수의 경우 일반적으로 결과를 재현 할 수 있기를 원합니다. 좋은 해답이 여기있다

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

,하지만 난 더 완전한 대답은 매우 간단하게 얻을 프로그래머를 명령형하는 것이 만들 수있는 방법으로, 하스켈에서 임의의 숫자를 사용하는 방법을 보여 것이라고 생각 예를 들면 다음과 같습니다이다. newRand 유형 IO Int이며하지 Int

import System.Random 
newRand = randomIO :: IO Int 

때문에,이 함수의 매개 변수로 사용할 수 없습니다

첫째, 당신은 임의의 씨앗이 필요합니다. (이것은 똑같은 결과를 항상 같은 입력으로 반환하는 순수 함수로서 Haskell 함수를 보존합니다.) 그러나 GHCI에 newRand을 입력하고 매번 고유 한 무작위 배정을 얻을 수 있습니다. 이는 newRandIO 유형이고 표준 (변경 불가능한) 변수 또는 함수가 아니기 때문에 가능합니다.

*Main> newRand 
-958036805781772734 

그런 다음이 시드 값을 복사하여 우리를위한 난수 목록을 만드는 함수에 붙여 넣을 수 있습니다.우리는 다음과 같은 기능을 정의하는 경우 : 주어진 씨앗에

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

를 붙여 넣기 기능은 GHCI에서 실행될 때 : 우리는 1 (전용) 0에서 익숙한 값을 얼마나

*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] 

알 수 있습니다. 우리는 명령형 언어와 같이 반복 할 때마다 새로운 난수를 생성하는 대신 사전에 난수 목록을 생성하고 각각의 연속 재귀에 대해 목록의 꼬리 부분을 사용합니다. 예 :

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]