2010-04-29 18 views
16

Mi sono scontrato con Haskell alcuni giorni e sono incappato in un problema.Haskell e numeri casuali

Ho bisogno di un metodo che restituisca un elenco casuale di numeri interi (Rand [[Int]]).

Quindi, ho definito un tipo: type Rand a = StdGen -> (a, StdGen). Sono stato in grado di produrre Rand IO Integer e Rand [IO Integer] ((returnR lst) :: StdGen -> ([IO Integer], StdGen)) in qualche modo. Qualche consiglio su come produrre Rand [[Int]]?

risposta

20

Come evitare il IO dipende dal motivo per cui viene introdotto in primo luogo. Sebbene i generatori di numeri pseudo casuali siano intrinsecamente orientati allo stato, non c'è motivo di coinvolgere lo IO.

Ho intenzione di indovinare e dire che stai usando newStdGen o getStdGen per ottenere il tuo PRNG iniziale. Se questo è il caso, non c'è modo di sfuggire completamente a IO. Si potrebbe invece seminare il PRNG direttamente con mkStdGen, tenendo presente che lo stesso seme produrrà la stessa sequenza numerica "casuale".

Più probabilmente, quello che vuoi fare è ottenere un PRNG all'interno di IO, quindi passarlo come argomento a una funzione pura. L'intero oggetto verrà comunque incapsulato in IO alla fine, ovviamente, ma i calcoli intermedi non ne avranno bisogno. Ecco un esempio veloce per darvi l'idea:

import System.Random 

type Rand a = StdGen -> (a, StdGen) 

getPRNG = do 
    rng <- newStdGen 
    let x = usePRNG rng 
    print x 

usePRNG :: StdGen -> [[Int]] 
usePRNG rng = let (x, rng') = randomInts 5 rng 
        (y, _) = randomInts 10 rng' 
       in [x, y] 

randomInts :: Int -> Rand [Int] 
randomInts 0 rng = ([], rng) 
randomInts n rng = let (x, rng') = next rng 
         (xs, rng'') = randomInts (n - 1) rng' 
        in (x:xs, rng'') 

si potrebbe notare che il codice utilizzando il PRNG diventa piuttosto brutto a causa passando il valore corrente avanti e indietro in continuazione. È anche potenzialmente soggetto a errori, dal momento che sarebbe facile riutilizzare accidentalmente un vecchio valore. Come accennato in precedenza, l'uso dello stesso valore PRNG darà la stessa sequenza di numeri, che di solito non è ciò che si desidera. Entrambi i problemi sono un perfetto esempio di dove è logico utilizzare una monade State - che qui è fuori discussione, ma potrebbe essere utile esaminarla successivamente.

3

Utilizzando la notazione monadica, si dovrebbe essere in grado di scrivere qualcosa di simile

randomList gen = do 
    randomLength <- yourRandomInteger 
    loop gen (randomLength + 1) 
where 
    loop gen 1 = gen 
    loop gen n = do { x <- gen; xs <- loop gen (n - 1); return (x:xs) } 

E con questo

randomInts :: Rand [Int] 
randomInts = randomList yourRandomInteger 

randomLists :: Rand [[Int]] 
randomLists = randomList randomInts 

Per quanto riguarda le monadici si calcoli, dare un'occhiata a this article. Si noti che i generatori casuali sono puri, non è necessario alcun IO solo per questo scopo.

+1

il momento in cui chiedi un campione, entri nella monade IO –

6

Se si desidera ottenere un infinita elenco di Integer s che si vuole incorrere in problemi come non sarà mai ottenere un valore StdGen di nuovo fuori. Quello che vuoi fare qui è split il StdGen prima, passare di nuovo a metà e "utilizzare" l'altra metà per generare una lista infinita di numeri interi. Qualcosa di simile a questo:

infiniteRandomInts :: Rand [Int] 
infiniteRandomInts g = (ints, g2) where 
    (g1,g2) = split g 
    ints = randoms g1 

Tuttavia, se si quindi ripetere questo approccio per ottenere una matrice infinita di Integer s (che ti sembra di volere, utilizzando Rand [[Int]]), si potrebbe incorrere in problemi di natura statistica: I Non so quanto bene StdGen si occupa di ripetuto split ting. Forse un'altra implementazione di RandomGen potrebbe essere migliore, oppure potresti provare a utilizzare una sorta di camminata diagonale per trasformare un [Int] in un [[Int]].

11

Si sta ricreando MonadRandom su Hackage. Se questo è più di un semplice esperimento per vedere se è possibile farlo, potresti invece usare quella libreria.