funcion - Número aleatorio en Haskell
funcion random haskell (5)
Esta pregunta ya tiene una respuesta aquí:
- Cómo obtener el valor normal de la acción IO en las respuestas de Haskell 2
Estoy tratando de obtener un número aleatorio en Haskell. (Lo que estoy aprendiendo actualmente y no he llegado a Monads o IO, etc.) el problema son las funciones en System.Random todas devuelven un IO Int, que luego no puedo usar en el resto de mi código que usa Int y flotador.
El objetivo aquí es elegir un par de una lista donde el primero del par es un flotador que representa una probabilidad. Así que mi plan era usar un número aleatorio para elegir un par en función de su probabilidad.
Como ya se dijo, los números aleatorios realmente no pueden ser valores puros 1 .
Sin embargo, esto no tiene por qué molestarte. Míralo a la inversa: otros idiomas simplemente no tienen valores puros, siempre se trata de interferencias con el mundo real. Haskell puede hacer eso también, en la mónada IO
. No es necesario que sepa exactamente cómo funciona eso, solo imite cómo se vería en un lenguaje de procedimiento (sin embargo, aquí hay algunos escollos).
Primero que todo necesitas un algoritmo, que no tiene nada que ver con el lenguaje en absoluto. La forma obvia es acumular las probabilidades en la lista y usar la función de pasos resultante como un mapa de [0, 1 [a sus valores deseados.
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
Tenga en cuenta que esto no maneja bien las entradas no válidas (las probabilidades no suman a 1, etc.) ni es eficiente, mezclando O ( n ) a través de acc''dList
para cada valor solicitado 2 . ¡Pero más importante, ten en cuenta que es una función pura ! En general, es una buena idea usar las funciones puras tanto como sea posible, y solo usar IO
cuando sea absolutamente necesario. Al igual que ahora: necesitamos obtener un valor Double
entre 0 y 1. ¡Fácil!
main = do
lookupVal <- randomRIO (0, 1)
print $ probsListLookup [(0.1, 1), (0.2, 2), (0.3, 4), (0.4, 5)] lookupVal
1 Al menos no de un tipo básico como Int
; Sin embargo, en realidad podría hacer "cálculos puros" en distribuciones de probabilidad total. Hacer eso explícitamente es muy engorroso, pero Haskell le permite usar hackage.haskell.org/package/MonadRandom-0.1.12/docs/… (o, de hecho, comonads ) para hacerlo tan fácil como lo es en Haskell IO (o en cualquier lenguaje impuro) pero sin los peligros de entrada / salida.
2 Podrías mejorar eso, por ejemplo, con Data.Map
.
Esta es una barrera común para los nuevos programadores de Haskell. Quieres escapar de IO, y te lleva un tiempo descubrir la mejor manera de hacerlo. El tutorial Learn You A Haskell tiene una buena explicación de una manera de generar números aleatorios usando la mónada estatal, pero aún debe ser sembrado usando getStdGen
o newStdGen
, que están en IO.
Para casos simples puedes hacer algo como
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
Como puede ver, puede obtener su número aleatorio en main
, luego pasar ese valor a una función pura que realiza el procesamiento.
Quizás se esté preguntando por qué hay todo este trabajo para generar números aleatorios en Haskell. Existen numerosas buenas razones, la mayoría de las cuales tienen que ver con el sistema de tipos. Dado que generar números aleatorios requiere modificar el estado del StdGen en el sistema operativo, tiene que vivir dentro de IO
, de lo contrario, podría tener una función pura que le dé resultados diferentes cada vez.
Imagina este escenario artificial:
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
Si lo ejecutó varias veces, es probable que termine "disparando todas las armas nucleares". Obviamente, esto es malo. myConstant
debería ser, bueno, constante, pero cada vez que ejecute el programa obtendría un valor diferente. Haskell quiere garantizar que una función pura siempre devolverá el mismo valor dadas las mismas entradas.
Puede ser molesto ahora mismo, pero es una herramienta poderosa en el kit del programador funcional.
Hay buenas respuestas aquí, pero sentí que una respuesta más completa mostraría muy simplemente cómo obtener y usar números aleatorios en Haskell, de una manera que tendría sentido para los programadores imperativos.
Primero, necesitas una semilla al azar:
import System.Random
newRand = randomIO :: IO Int
Como newRand
es de tipo IO Int
y no Int
, no se puede utilizar como parámetro de función. (Esto conserva las funciones de Haskell como funciones puras que siempre devolverán el mismo resultado en la misma entrada).
Sin embargo, podemos simplemente ingresar newRand
en GHCI y obtener una semilla aleatoria única cada vez. Esto es posible solo porque newRand
es de tipo IO
y no es una función o variable estándar (inmutable).
*Main> newRand
-958036805781772734
Luego podemos copiar y pegar este valor semilla en una función que crea una lista de números aleatorios para nosotros. Si definimos la siguiente función:
randomList :: Int -> [Double]
randomList seed = randoms (mkStdGen seed) :: [Double]
Y pegue la semilla dada cuando la función se ejecute en GHCI:
*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]
Observe cómo obtenemos los valores familiares de 0 a 1 (exclusivo). En lugar de generar un nuevo número aleatorio cada iteración como lo haríamos en un lenguaje imperativo, generamos una lista de números aleatorios con anticipación y usamos el encabezado de la cola de la lista en cada recursión sucesiva. Un ejemplo:
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
Crear dos listas con anticipación y proporcionarlas como parámetros nos permite buscar el (uno y único) triple de Pitágoras donde a + b + c = 1000. Por supuesto, querría usar una semilla aleatoria diferente para cada lista:
*Main> newRand
3869386208656114178
*Main> newRand
-5497233178519884041
*Main> list1 = randomList 3869386208656114178
*Main> list2 = randomList (-5497233178519884041)
*Main> pythagCheck list1 list2
[200,375,425]
No creo que estas respuestas sean el cuadro completo. Para mis simulaciones genero perezosamente números aleatorios y los consumo estrictamente ejecutándolos en un espacio reducido (1.1M en mi macbook).
Quizás las observaciones de que los números aleatorios solo pueden existir en la mónada IO se refieren a números realmente aleatorios, pero para los números pseudoaleatorios no es el caso y, por lo general, uno quiere poder reproducir los resultados. Aquí hay un ejemplo:
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
Si desea una verdadera aleatoriedad, no podrá usar IO, suena como un arrastre, pero esta separación es un aspecto realmente importante de Haskell. Sin embargo, puede obtener una semi-pseudoaleatoriedad seleccionando una "semilla" por usted mismo y utilizando las funciones puras de System.Random que devuelven un par de resultados y una nueva semilla (por ejemplo, "random").