Komputery są deterministyczne i nie mogą generować liczb losowych. Raczej opierają się na matematycznych formułach, które zwracają rozkład liczb, które wyglądają losowo.Są to tak zwane generatory liczb pseudolosowych. Jednak z powodu determinizmu mamy problem, że gdybyśmy podczas tych inwokacji naszego programu uruchamiali te formuły w ten sam sposób, otrzymalibyśmy te same generatory liczb losowych. Oczywiście nie jest to dobre, ponieważ chcemy, aby nasze liczby były losowe! W związku z tym musimy dostarczyć generatorowi losowemu początkową wartość początkową, która zmienia się z biegu do uruchomienia. Dla większości ludzi (tj. Tych, którzy nie robią rzeczy kryptograficznych) generator liczb losowych jest zarzucany przez bieżący czas. W Haskell ten pseudolosowy generator jest reprezentowany przez typ StdGen
. Funkcja mkStdGen
służy do tworzenia generatora liczb losowych z materiałem siewnym. W przeciwieństwie do C, gdzie istnieje jeden globalny generator liczb losowych, w Haskell możesz mieć tylu, ile chcesz, i możesz tworzyć je z różnymi nasionami.
Jednak jest pewne zastrzeżenie: ponieważ liczby są pseudolosowe, nie ma gwarancji, że generatory liczb losowych utworzone z różnych liczb zwracających nasiona wyglądają losowo w porównaniu do innych. Oznacza to, że po wywołaniu randomBool
i nadaniu jej kolejnych wartości początkowych, nie ma gwarancji, że liczba uzyskana z utworzonego przez użytkownika StdGen
jest losowa w porównaniu z StdGen
wysianą z jej następcą. Dlatego otrzymujesz prawie 50000 True
.
Aby uzyskać dane wyglądające losowo, należy nadal korzystać z tego samego generatora liczb losowych. Jeśli zauważysz, funkcja Haskell random
ma typ StdGen -> (a, StdGen)
. Ponieważ Haskell jest czysty, funkcjapobiera generator liczb losowych, generuje wartość pseudolosową (pierwszy element wartości zwracanej), a następnie zwraca nową StdGen
, która reprezentuje generator zaszczepiony oryginalnym nasieniem, ale gotowa do podania nowa liczba losowa. Musisz zachować ten inny numer StdGen
i przekazać go do następnej funkcji random
, aby uzyskać losowe dane.
Oto przykład generowania trzech losowych sygnałów, a
, b
i c
.
randomBools :: StdGen -> (Bool, Bool, Bool)
randomBools gen = let (a, gen') = random gen
(b, gen'') = random gen''
(c, gen''') = random gen'''
in (a, b, c)
Wskazówki jak zmienna gen
jest „gwintowane” za pośrednictwem połączeń do losowo.
Można uprościć przekazywanie stanu za pomocą monady stanu. Na przykład,
import Control.Monad.State
import System.Random
type MyRandomMonad a = State StdGen a
myRandom :: Random a => MyRandomMonad a
myRandom = do
gen <- get -- Get the StdGen state from the monad
let (nextValue, gen') = random gen -- Generate the number, and keep the new StdGen
put gen' -- Update the StdGen in the monad so subsequent calls generate new random numbers
return nextValue
Teraz można napisać funkcję randomBools
jak:
randomBools' :: StdGen -> (Bool, Bool, Bool)
randomBools' gen = fst $ runState doGenerate gen
where doGenerate = do
a <- myRandom
b <- myRandom
c <- myRandom
return (a, b, c)
Jeśli chcesz wygenerować (skończoną) wykaz Bool
s, można zrobić
randomBoolList :: StdGen -> Int -> ([Bool], StdGen)
randomBoolList gen length = runState (replicateM length myRandom) gen
Zwróć uwagę, jak zwracamy StdGen
jako drugi element zwróconej pary, aby umożliwić jej udostępnienie nowych funkcji.
Po prostu, jeśli chcesz wygenerować nieskończoną listę losowych wartości tego samego typu z StdGen
, możesz użyć funkcji randoms
. To ma podpis (RandomGen g, Random a) => g -> [a]
. Aby wygenerować nieskończoną listę Bool
przy użyciu początkowego seeda x
, wystarczy uruchomić randoms (mkStdGen x)
. Możesz zaimplementować swój przykład za pomocą length $ takeWhile id (randoms (mkStdGen x))
. Powinieneś zweryfikować, że otrzymujesz różne wartości dla różnych początkowych wartości x
, ale zawsze ta sama wartość, jeśli dostarczasz tę samą x
.
Wreszcie, jeśli nie interesuje Cię bycie związanym z monadą IO
, Haskell dostarcza również globalny generator liczb losowych, podobnie jak języki imperatywne. Wywołanie funkcji randomIO
w monadzie da ci losową wartość dowolnego typu, który ci się podoba (o ile jest to przynajmniej egzemplarz modelu typograficznego Random
). Możesz użyć tego podobnie do myRandom
powyżej, z wyjątkiem monady IO
. Ma to dodatkową zaletę, że jest wstępnie zaimplementowany w środowisku wykonawczym Haskell, co oznacza, że nie musisz się nawet martwić o utworzenie StdGen
. Tak więc, aby stworzyć losową listę 10 Bool
S w IO
monady, wszystko co musisz zrobić, to replicateM 10 randomIO :: IO [Bool].
Nadzieja to pomaga :)
Aby rozwinąć to: losowy generator nie jest gwarantowana mieć losowy rozkład na wartości początkowe, ale gwarantuje się losowy rozkład na sekwencje wytworzonych wartości. – mange