如何在Haskell中在没有IO的情况下生成随机数?

时间:2019-10-01 04:42:02

标签: haskell random

我想生成一个范围内的随机数,类型签名为Int -> Int。我已经阅读了多篇其他文章,但没有一篇文章建议返回类型Int的方法。我在代码中使用了System.IO.Unsafe,但不建议这样做。这是我的代码:

import System.IO.Unsafe

-- random number generator
rng :: Int -> Int
rng upper = unsafePerformIO $ randomRIO (0,upper-1) 

有人对在Haskell范围内如何生成随机Int有何建议?

编辑:可能无法更改IO Int -> Int,所以我将代码转换为

-- random number generator
rng :: Int -> IO Int
rng upper = randomRIO (0,upper-1) 

之所以需要rng是因为我想获取列表范围长度内的随机数,以获得列表元素的索引。

list !! rng (length list),但出现错误Couldn't match expected type ‘Int’ with actual type ‘IO Int’,这是预期的。

这不是重复项,因为1.我想要一个范围内的值,2.我的rng不返回相同的值。我是Haskell的新手,我不知道如何操纵Monad。任何帮助表示赞赏。

2 个答案:

答案 0 :(得分:6)

我本着https://xkcd.com/221/的精神,这是一个没有任何IO的“解决方案”:

rng :: Int -> Int
rng upper
 | upper<=4   = upper
 | otherwise  = 4

因此,您可以得到一个“符合RFC 1149.5的随机数”。除非超出范围,否则总是4。

这是什么问题?好吧,很明显,它总是给出相同的数字 –所以它必须是,因为所有Haskell函数必须为functions,即referentially transparent。 OTOH,随机数生成器应该在您每次调用它时都赋予不同的数字...因此,它不是 一个函数,并且大多数其他编程语言只是假装它是具有副作用的函数 –因为它们没有适当的方式来表示副作用是什么。好吧,Haskell确实有一种适当的表达方式,这就是IO monad:您可以进行依赖于副作用的计算,但是显然,如果运行这些计算,它们本身将具有副作用。
因此,签名Int -> IO Int对于该功能确实有意义。 (此函数,但是结果是 IO操作,只有执行该操作会给您Int。)< / p>

这很丑陋,因为IO Int可以在IO中执行 的任何操作–例如,它可以launch some missiles并返回给您伤亡。实际上,它可以轻松地修改主目录中的某些文件。而您实际上想要的只是很小的,无害的副作用,足以在下一次产生新的随机数。通常,随机数生成器并不是真正的随机生成器,而是PRNGs,它会保留一个恒定大小的状态变量,该变量在每次提取值时都会以一种随机的方式进行更新。下次,此状态将有所不同,因此您将根据需要获得不同的值。此状态变量可以保留在IO可变位置

import Data.IORef

type RandStV = Int
type RandSt = IORef RandStV

rng' :: RandSt -> Int -> IO Int
rng' rSt upper = do
   x <- readIORef rSt
   let x' = ((x * 1103515245) + 12345) `mod` 0x7fffffff -- https://sourceware.org/git/?p=glibc.git;a=blob;f=stdlib/random_r.c;hb=glibc-2.26#l362
   writeIORef rSt x'
   return $ x `mod` upper

...或者,您可以将更新的状态与结果一起明确传递

rng'' :: Int -> RandStV -> (RandStV, Int)
rng'' upper x =
   let x' = ((x * 1103515245) + 12345) `mod` 0x7fffffff
   in (x', x `mod` upper)

...或者可以在专用的state monad中进行传递,这是编写更新变量传递的另一种方式:

type RandStM = State RandStV

rng''' :: Int -> RandStM Int
rng''' upper = do
   x <- get
   let x' = ((x * 1103515245) + 12345) `mod` 0x7fffffff
   put x'
   return $ x `mod` upper

请参阅the random-fu package,以获取有关这样的 random monad 的有用帮助程序。

解释rng'''的一种数学方法是说它是一个以上限为参数并返回数字distribution的函数。分布总是相同的,但是它“包含”许多数字以及它们出现的可能性。实际上,生成一个整数表示您正在从分布中进行采样

答案 1 :(得分:2)

没有使用IO时,Haskell不能生成随机数。

您的示例list !! rng (length list)不起作用,因为rng返回IO Int并且!!期望Int

以下是使用您的rng函数从列表中获取随机元素的函数:

-- Will crash on empty list
randomElementFromList :: [a] -> IO a
randomElementFromList list = do
  r <- rng (length list)
  return $ list !! r