Generate a random integer in a range in Haskell without a seed

前端 未结 5 1299
自闭症患者
自闭症患者 2020-12-14 08:52

How can I generate a random number in Haskell from a range (a, b) without using any seed?

The function should return an Int and not an IO Int. I have a function X t

5条回答
  •  Happy的楠姐
    2020-12-14 08:56

    I used SipHash for that purpose

    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)
    

    the read and drop 8 and show serve the purpose to drop a newtype that doesn’t (or didn’t when I implemented this) support any casting

    now you want an Int in a range. Integer is easier though:

    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
    

    of course, you’ll still get the same number for the same arguments every time, so you’ll need to vary them ie you still pass around arguments, just not returned values too. Whether that’s more convenient than a monad depends (for my purpose it was)

    this is how I made sure arguments (specifically the [Word8] argument) would always be different:

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

提交回复
热议问题