2013-01-07 3 views
13

Sto usando System.Random e la classe Random nella mia applicazione per generare numeri casuali. Comunque mi piacerebbe per generare un elenco dei Carri casuali di lunghezza arbitraria con una funzione come randoms :: StdGen -> Int -> ([Float], StdGen)Genera lista di valori casuali e ottieni anche un nuovo generatore

Senza il vincolo di ottenere un nuovo generatore, ho potuto facilmente scrivere randoms gen n = (take n $ randoms gen) :: [Float]

Tuttavia, questo mi lascia con la stessa generatore casuale ho iniziato con, il che significa che se dovessi eseguire questa funzione due volte di seguito otterrei la stessa lista a meno che non andassi a usare il generatore altrove per ottenerne uno nuovo.

Come posso generare una lista infinita (o di lunghezza arbitraria) di valori casuali mentre "rinfresca" il mio generatore casuale.

+1

Un'altra opzione è quella di ['split'] (http://hackage.haskell.org/packages/archive/random/latest/doc/html/System-Random.html#v:split) il generatore in due. Quindi puoi usare uno dei generatori risultanti per chiamare 'randoms' e continuare con l'altro. Questo funziona anche con liste infinite. – hammar

risposta

28

Bene, diamo un'occhiata alla funzione che si ha:

random :: StdGen -> (Float, StdGen) -- From System.Random 

Possiamo avvolgere questo in State Monade per ottenere un calcolo stateful:

state :: (s -> (a, s)) -> State s a -- From Control.Monad.Trans.State 

random' :: State StdGen Float 
random' = state random 

Ora, siamo in grado di generare un mucchio di carri semplicemente usando replicateM:

replicateM :: (Monad m) => Int -> m a -> m [a] -- From Control.Monad 

randoms' :: Int -> State StdGen [Float] 
randoms' n = replicateM n random' 

Infine, scartare i State per tornare al generatore esplicito passaggio:

randoms :: Int -> StdGen -> ([Float], StdGen) 
randoms n = runState (randoms' n) 

Se si uniscono tutti questi in una definizione di funzione che si ottiene:

randoms :: Int -> StdGen -> ([Float], StdGen) 
randoms n = runState (replicateM n (state random)) 

In altre parole, siamo in grado di descrivere il processo come:

  • avvolgere random nel State monade
  • replicare n volte
  • scartarlo

Questo è il motivo per cui le monadi sono un concetto così importante. Le cose che possono sembrare complicate inizialmente tendono ad essere semplici calcoli se osservate attraverso l'obiettivo dell'interfaccia monad.

3

Un'alternativa senza State o split, utilizzando mapAccumL da Data.List (e swap da Data.Tuple):

nRandoms n gen = mapAccumL(\g _ -> swap $ random g) gen [1..n] 

anche se devo dire che non ho un argomento convincente per il motivo per cui questo dovrebbe essere meglio in qualsiasi modo.

+0

È 'swap' nelle librerie standard? –

+0

@BenMillwood 'Data.Tuple'. È stato recentemente aggiunto, non ricordo come recentemente esattamente. Sembrava: ha fatto la sua prima apparizione in ghc-7.0. *. Quindi non estremamente recente. –

5

La risposta di Gabriel è corretta e questo è praticamente il modo in cui viene implementato il pacchetto MonadRandom (stato Monad parametrizzato con un generatore casuale).

Ti fa risparmiare ogni volta la definizione e viene fornito con un trasformatore Monad, così puoi trasformare qualsiasi altra Monade in una che può anche produrre valori casuali.

Il vostro esempio potrebbero essere facilmente implementato come:

(runRand $ take n `fmap` getRandoms) :: RandomGen g => g -> ([Int], g) 

StdGen sembra essere un esempio di di randomgen, in modo da poter semplicemente inserire la spina e andare!

1

È possibile definire una funzione il cui tipo corrisponde a quello che si desidera avere, anche se più in generale.

import System.Random 

randoms' :: (RandomGen g, Random a) => g -> Int -> ([a], g) 
randoms' g n = 
    let (g1, g2) = split g 
    in (take n $ randoms g1, g2) 

Anche se utilizza split

split :: g -> (g, g)

L'operazione split permette di ottenere due distinti generatori di numeri casuali. Questo è molto utile nei programmi funzionali (ad esempio, quando si passa un generatore di numeri casuali alle chiamate ricorsive), ma è stato fatto pochissimo lavoro su implementazioni statisticamente valide di split e hellip;

ancora non fa quello che vuoi. (Uso Bool negli esempi di seguito facilitano il confronto visivo.)

ghci> g <- getStdGen 
ghci> randoms' g 5 :: ([Bool], StdGen) 
([False,False,False,True,False],1648254783 2147483398) 
ghci> randoms' g 5 :: ([Bool], StdGen) 
([False,False,False,True,False],1648254783 2147483398) 

noti che le matrici casuali sono uguali.

Sebbene la funzione risolva il rischio di dividere il generatore, lo scartiamo prontamente. Invece, fanno uso di g2 infilando alla chiamata successiva come in

ghci> let (a1,g2) = randoms' g 5 :: ([Bool], StdGen) 
ghci> let (a2,_) = randoms' g2 5 :: ([Bool], StdGen) 
ghci> (a1,a2) 
([False,False,False,True,False],[True,True,True,False,True] 

Se il codice viene eseguito nel IO Monade, è possibile utilizzare setStdGen per sostituire il generatore globale di numeri casuali, alla fine, come in

myAction :: Int -> IO ([Float],[Float]) 
myAction n = do 
    g <- getStdGen 
    let (f1,g2) = randoms' g n 
    let (f2,g3) = randoms' g2 n 
    setStdGen g3 
    return (f1, f2) 

Lo stato di threading è scomodo e soggetto a errori. Prendi in considerazione l'utilizzo di State o ST se disponi di un numero di piastre ripetuto.

Problemi correlati