2011-12-07 98 views
17

¿Cómo puedo generar un número aleatorio en Haskell desde un rango (a, b) sin usar ninguna semilla?Generar un entero aleatorio en un rango en Haskell

La función debe devolver un Int y no un IO Int. Tengo una función X que toma e Int y otros argumentos y produce algo que no es un IO.

Si esto no es posible, ¿cómo puedo generar una semilla usando la biblioteca Time y generar un número aleatorio en el rango con mkStdGen?

Cualquier ayuda sería muy apreciada.

+1

http: // stackoverflow .com/a/2738824/570689 - buena respuesta para una pregunta similar –

+4

Parece que lo mejor que puedes esperar es 'randomInt lo hi = lo' o algo por el estilo. Sin algún tipo de semilla o IO, la función tiene que devolver el mismo valor cada vez. Y el límite inferior es tan aleatorio como cualquier otro 'Int'. :) – augustss

+2

"Cualquiera que considere métodos aritméticos para producir dígitos aleatorios está, por supuesto, en estado de pecado". Atribuido a John Von Neumann en Knuth, D. _The Art of Computer Programming_, vol 2, p. 1 (1981). – rickythesk8r

Respuesta

36

Una función no puede devolver Int sin IO, a menos que se trate de una función pura, es decir, con la misma entrada siempre obtendrá la misma salida. Esto significa que si quiere un número aleatorio sin IO, tendrá que tomar una semilla como argumento.

  • Si usted decide tomar una semilla, que debe ser de tipo StdGen, y se puede utilizar randomR para generar un número de ella. Use newStdGen para crear una nueva semilla (esto tendrá que hacerse en IO).

    > import System.Random 
    > g <- newStdGen 
    > randomR (1, 10) g 
    (1,1012529354 2147442707) 
    

    El resultado de randomR es una tupla en el que el primer elemento es el valor aleatorio, y la segunda es una nueva semilla de utilizar para generar más valores.

  • De lo contrario, puede utilizar randomRIO para obtener un número aleatorio directamente en la IO mónada, con todo el cuidado StdGen cosas por usted:

    > import System.Random 
    > randomRIO (1, 10) 
    6 
    
+6

Para aquellos que hacen esto, no olviden 'importar System.Random'! –

4

Sin recurrir a todo tipo de inseguridad prácticas, no es posible que dicha función tenga el tipo Int en lugar del tipo IO Int o algo similar. Las funciones (o, en este caso, las constantes) del tipo Int son puras, lo que significa que cada vez que "invoque" la función (recupere el valor de la constante) se garantiza que obtendrá el mismo valor "devuelto".

Si desea que se devuelva un valor diferente, elegido al azar en cada invocación, deberá usar el IO -monad.

En algunas ocasiones, es posible que desee tener un único valor producido al azar para todo el programa, es decir, uno que, desde la perspectiva del programa, se comporte como si fuera un valor puro. Cada vez que consulta el valor dentro de la misma ejecución del programa, obtiene el mismo valor. Como todo el programa es esencialmente una acción IO, podrías generar ese valor una vez y pasarlo, pero esto puede parecer un poco torpe. Se podría argumentar que, en esta situación, todavía es seguro para asociar el valor con una constante de nivel superior de tipo Int y utilizar unsafePerformIO para construir esa constante:

import System.IO.Unsafe -- be careful!           
import System.Random 

-- a randomly chosen, program-scoped constant from the range [0 .. 9]    
c :: Int 
c = unsafePerformIO (getStdRandom (randomR (0, 9))) 
+32

¡No hagas esto! – augustss

+4

Especialmente, no haga esto sin leer cuidadosamente los consejos y precauciones mencionados en http://www.haskell.org/ghc/docs/latest/html/libraries/base/System-IO-Unsafe.html. –

+0

@dblhelix Su enlace está muerto – Muhd

1
fmap yourFunctionX $ randomRIO (a, b) 

o

fmap (\x -> yourFunctionX aParam x anotherParam) $ randomRIO (a, b) 

El resultado será entonces del tipo IO whateverYourFunctionXReturns.

Si import Control.Applicative, se puede decir

yourFunctionX <$> randomRIO (a, b) 

o

(\x -> yourFunctionX aParam x anotherParam) <$> randomRIO (a, b) 

la que se puede encontrar más clara

1

Tenga en cuenta que se puede obtener una lista infinita de valores aleatorios utilizando la mónada IO y use ese [Int] en funciones que no sean IO. De esta forma, no tiene que llevar la semilla junto con usted, pero aún así debe llevar la lista, por supuesto. Afortunadamente, hay muchas funciones de procesamiento de listas para simplificar dicho subprocesamiento, y aún puede usar la mónada State en casos complicados.

También tenga en cuenta que puede convertir fácilmente un IO Int a un Int. Si foo produce un IO Int, y el bar tiene un Int como su único parámetro y devuelve un valor distinto de IO, lo siguiente será hacer:

foo >>= return . bar 

O usando la notación do:

do 
    a <- foo 
    return $ bar a 
0

Solía SipHash para ese propósito

import Data.ByteArray.Hash 
import Data.ByteString (pack, cons) 
import Data.Word (Word8, Word64) 

random :: Word64 -> Word64 -> [Word8] -> Double 
random a b cs = (subtract 1) . (/(2**63)) . read . drop 8 . show $ sipHash (SipKey a b) (pack cs) 

la lectura y soltar 8 y espectáculo de servir al propósito para perder un newtype que no lo hace (o no hizo cuando he implementado este) apoya ninguna de fundición

ahora quiere un int en un rango. Entero es más fácil sin embargo:

random :: Word64 -> Word64 -> [Word8] -> (Integer, Integer) -> Integer 
random a b cs (low,high) = let 
    span = high-low 
    rand = read . drop 8 . show $ sipHash (SipKey a b) (pack cs) 
    in (rand `mod` span) + low 

por supuesto, usted todavía tendrá el mismo número de los mismos argumentos cada vez, por lo que tendrá que variar ellas, es decir, que todavía pasa alrededor de los argumentos, los valores devueltos no demasiado . Si eso es más conveniente que una mónada depende (para mi propósito era)

esto es cómo hice argumentos seguro (en concreto el [Word8] argumento) siempre sería diferente:

foo bytes = doSomethingRandom bytes 
bar bytes = map (\i -> foo (i:bytes)) [1..n] 
baz bytes = doSomething (foo (0:bytes)) (bar (1:bytes)) 
Cuestiones relacionadas