2011-12-07 15 views
17

Jak wygenerować liczbę losową w Haskell z zakresu (a, b) bez użycia nasion?Generowanie losowej liczby całkowitej w zakresie w Haskell

Funkcja powinna zwrócić wartość Int, a nie Int IO. Mam funkcję X, która pobiera i Int i inne argumenty i wyniki coś, co nie jest IO.

Jeśli nie jest to możliwe, w jaki sposób wygenerować materiał siewny za pomocą biblioteki Czas i wygenerować liczbę losową w zakresie za pomocą polecenia mkStdGen?

Każda pomoc będzie naprawdę doceniana.

+1

http: // stackoverflow .com/a/2738824/570689 - dobra odpowiedź na podobne pytanie –

+4

Brzmi jak najlepsze, na co można liczyć, to "randomInt lo hi = lo" lub coś podobnego. Bez jakiegoś nasienia lub IO funkcja musi zawsze zwracać tę samą wartość. A dolna granica jest tak samo losowa, jak każde inne "Int". :) – augustss

+2

"Każdy, kto rozważa arytmetyczne metody wytwarzania losowych cyfr, jest oczywiście w stanie grzechu." Przypisany Johnowi Vonowi Neumannowi _ in_ Knuth, D. _ The Art of Computer Programming_, tom 2, s. 1 (1981). – rickythesk8r

Odpowiedz

36

Funkcja nie może zwrócić wartości Int bez IO, o ile nie jest to czysta funkcja, to znaczy z uwagi na to samo wejście zawsze otrzymamy takie samo wyjście. Oznacza to, że jeśli chcesz mieć liczbę losową bez numeru IO, musisz wziąć seed jako argument.

  • Jeśli zdecydujesz się wziąć ziarno powinno być typu StdGen, i można użyć randomR wygenerować numer z nim. Użyj newStdGen, aby utworzyć nowy materiał siewny (należy to zrobić w IO).

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

    Wynikiem randomR jest krotka, w których pierwszy element jest wartością losową, a druga jest nowy nasion użyć do generowania wielu wartości.

  • W przeciwnym razie, można użyć randomRIO aby uzyskać liczbę losową bezpośrednio w IO monady, ze wszystkimi StdGen rzeczy pod opieką dla Ciebie:

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

Dla tych, którzy to robią, nie zapomnij 'import System.Random'! –

4

bez uciekania się do wszelkiego rodzaju niebezpieczne nie jest możliwe, aby taka funkcja miała raczej typ Int niż typ IO Int lub coś podobnego. Funkcje (lub, w tym przypadku, stałe) typu Int są czyste, co oznacza, że ​​za każdym razem, gdy "wywołasz" funkcję (odczytaj wartość stałej), masz gwarancję otrzymania tej samej wartości "zwróconej".

Jeśli chcesz mieć inną, losowo wybraną wartość zwracaną przy każdym wywołaniu, będziesz musiał użyć zmiennej -monad IO.

W niektórych przypadkach może być potrzebna pojedyncza losowo wytworzona wartość dla całego programu, tj. Taki, który z perspektywy programu zachowuje się tak, jakby był czystą wartością. Za każdym razem, gdy wyszukujesz wartość w tym samym przebiegu programu, odzyskujesz tę samą wartość. Ponieważ cały program jest w istocie wynikiem działania w postaci IO, możesz raz wygenerować tę wartość i przekazać ją dookoła, ale może to być trochę niezręczne. Można argumentować, że w tej sytuacji, to jest wciąż bezpieczny powiązać wartości ze stałą najwyższego poziomu typu Int i używać unsafePerformIO skonstruować że Constant:

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

Nie rób tego !! – augustss

+4

W szczególności nie rób tego bez uważnego przeczytania porad i środków ostrożności wymienionych na stronie http://www.haskell.org/ghc/docs/latest/html/libraries/base/System-IO-Unsafe.html. –

+0

@dblhelix Twój link jest martwy – Muhd

1
fmap yourFunctionX $ randomRIO (a, b) 

lub

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

Wynik będzie wówczas miał typ IO whateverYourFunctionXReturns.

jeśli import Control.Applicative, można powiedzieć

yourFunctionX <$> randomRIO (a, b) 

lub

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

które można znaleźć jaśniejsze

1

pamiętać, że można uzyskać nieskończoną listę losowych wartości z wykorzystaniem Monada IO i użyj tego [Int] w funkcjach innych niż IO. W ten sposób nie musisz nosić nasiona razem z tobą, ale nadal musisz mieć listę rzeczy do zrobienia. Na szczęście istnieje wiele funkcji przetwarzania list, które upraszczają takie wątki i nadal można korzystać z monady State w skomplikowanych przypadkach.

Należy również pamiętać, że można łatwo przekonwertować IO Int na Int. Jeśli foo produkuje IO Int i bar bierze Int jako jedyny parametr i zwraca wartość non-IO, obowiązują następujące czynności:

foo >>= return . bar 

lub używając zrobić notacji:

do 
    a <- foo 
    return $ bar a 
0

użyłem SipHash do tego celu

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) 

odczytu i upuść 8 i pokaz służą do spadku newtype że nie (lub nie, kiedy to realizowane) wspierać wszelkie odlewy

Teraz chcesz Int w zakresie. Integer jest łatwiejsze choć:

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 

oczywiście, nadal trzeba uzyskać ten sam numer dla tych samych argumentów za każdym razem, więc trzeba je zmieniać, czyli nadal przechodzić wokół argumentów, po prostu nie zwracanych wartości zbyt . Czy to bardziej wygodne niż monada zależy (do moich celów było)

to jak ja zadbali argumenty (w szczególności [Word8] argument) zawsze będzie inna:

foo bytes = doSomethingRandom bytes 
bar bytes = map (\i -> foo (i:bytes)) [1..n] 
baz bytes = doSomething (foo (0:bytes)) (bar (1:bytes)) 
Powiązane problemy