2010-04-29 65 views
16

He estado jugando con Haskell hace unos días y tropecé con un problema.Haskell y números aleatorios

Necesito un método que devuelva una lista aleatoria de enteros (Rand [[Int]]).

Entonces, definí un tipo: type Rand a = StdGen -> (a, StdGen). Pude producir Rand IO Integer y Rand [IO Integer] ((returnR lst) :: StdGen -> ([IO Integer], StdGen)) de alguna manera. ¿Algún consejo sobre cómo producir Rand [[Int]]?

Respuesta

20

Cómo evitar el IO depende de por qué se está introduciendo en primer lugar. Si bien los generadores de números pseudoaleatorios están inherentemente orientados al estado, no hay ninguna razón por la cual se deba involucrar IO.

Voy a adivinar y decir que está usando newStdGen o getStdGen para obtener su PRNG inicial. Si ese es el caso, entonces no hay forma de escapar por completo IO. En su lugar, puede sembrar el PRNG directamente con mkStdGen, teniendo en cuenta que la misma semilla dará como resultado la misma secuencia numérica "aleatoria".

Lo más probable es que lo que quiere hacer es obtener un PRNG dentro de IO, luego pase eso como un argumento a una función pura. Toda la cosa todavía estará envuelta en IO al final, por supuesto, pero los cálculos intermedios no lo necesitarán. Aquí está un ejemplo rápido para darle la 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'') 

usted podría notar que el código utilizando el PRNG se pone muy fea debido a que pasa el valor actual de ida y vuelta constante. También es potencialmente propenso a errores, ya que sería fácil reutilizar accidentalmente un valor anterior. Como se mencionó anteriormente, usar el mismo valor de PRNG dará la misma secuencia de números, que generalmente no es lo que desea. Ambos problemas son un ejemplo perfecto de dónde tiene sentido usar una mónada State, que se está desviando del tema aquí, pero es posible que desee analizarla a continuación.

3

Utilizando la notación monádico, que debe ser capaz de escribir algo como

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) } 

Y con esta

randomInts :: Rand [Int] 
randomInts = randomList yourRandomInteger 

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

En cuanto a la propia cálculos monádicos, echar un vistazo a this article. Tenga en cuenta que los generadores aleatorios son puros, no necesita ningún IO solo para este propósito.

+1

el momento en que pides una muestra, obtienes la mónada IO –

6

Si desea obtener una infinita lista de Integer s que vas a tener problemas ya que no será nunca obtener un valor StdGen de vuelta. Lo que quiere hacer aquí es split el StdGen primero, pase la mitad otra vez y "agote" la otra mitad para generar una lista infinita de enteros. Algo como esto:

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

Sin embargo, si a continuación, repita este método para obtener una matriz infinita de Integer s (que parece que quieres, mediante el uso de Rand [[Int]]), usted podría encontrarse con problemas de naturaleza estadística: I no sé qué tan bien StdGen se ocupa de repetir split ting. Quizás otra implementación de RandomGen sea mejor, o podría intentar usar algún tipo de caminata diagonal para convertir un [Int] en un [[Int]].

11

Usted está recreando MonadRandom en Hackage. Si esto es más que un simple experimento para ver si puede hacerlo, es posible que desee utilizar esa biblioteca en su lugar.