生成随机值列表,并获得新的生成器

时间:2013-01-07 20:39:40

标签: haskell random

我在我的应用程序中使用System.RandomRandom类型类来生成随机数。但是我想用randoms :: StdGen -> Int -> ([Float], StdGen)

之类的函数生成任意长度的随机浮点数列表

没有获得新发电机的限制,我可以轻松写 randoms gen n = (take n $ randoms gen) :: [Float]

然而,这让我开始使用相同的随机生成器,这意味着如果我连续两次运行此函数,我将获得相同的列表,除非我去其他地方使用生成器来获取新的生成器。

如何生成无限(或任意长度)随机值列表,同时“刷新”我的随机生成器。

4 个答案:

答案 0 :(得分:28)

好吧,让我们来看看你的功能:

random :: StdGen -> (Float, StdGen)  -- From System.Random

我们可以将它包装在State monad中以获得有状态计算:

state :: (s -> (a, s)) -> State s a  -- From Control.Monad.Trans.State

random' :: State StdGen Float
random' = state random

现在,我们可以使用replicateM生成一堆浮点数:

replicateM :: (Monad m) => Int -> m a -> m [a]  -- From Control.Monad

randoms' :: Int -> State StdGen [Float]
randoms' n = replicateM n random'

最后,我们打开State以取回显式生成器传递:

randoms :: Int -> StdGen -> ([Float], StdGen)
randoms n = runState (randoms' n)

如果将所有这些组合成一个函数定义,则得到:

randoms :: Int -> StdGen -> ([Float], StdGen)
randoms n = runState (replicateM n (state random))

换句话说,我们可以将过程描述为:

  • random monad
  • 中打包State
  • 复制n
  • 打开它

这就是为什么monads是如此重要的概念。当通过monad界面的镜头观看时,最初看似棘手的事情往往是简单的计算。

答案 1 :(得分:5)

Gabriel的回答是正确的,这几乎是MonadRandom包的实现方式(使用随机生成器参数化的Monad状态)。

它可以节省你每次定义它,它也带有一个Monad变换器,所以你可以将任何其他Monad转换为一个也可以产生随机值的Monad。

您的示例可以轻松实现为:

(runRand $ take n `fmap` getRandoms) :: RandomGen g => g -> ([Int], g)

StdGen恰好是RandomGen的一个实例,所以你可以简单地将其插入并继续!

答案 2 :(得分:3)

不使用Statesplit的替代方案,使用mapAccumL中的Data.List(以及来自swap的{​​{1}}):

Data.Tuple

虽然我不得不说我没有一个令人信服的论据,为什么这应该以任何方式更好。

答案 3 :(得分:1)

您可以定义一个类型与您想要的类型匹配的函数,尽管更常见。

import System.Random

randoms' :: (RandomGen g, Random a) => g -> Int -> ([a], g)
randoms' g n =
  let (g1, g2) = split g
  in (take n $ randoms g1, g2)

即使它使用split

  

split :: g -> (g, g)

     

split操作允许获得两个不同的随机数生成器。这在函数式程序中非常有用(例如,将随机数生成器传递给递归调用时),但在split的统计上强大的实现方面做的工作很少......

它仍然没有做你想要的。 (我在下面的示例中使用Bool以便于进行视觉比较。)

ghci> g <- getStdGen
ghci> randoms' g 5 :: ([Bool], StdGen)
([False,False,False,True,False],1648254783 2147483398)
ghci> randoms' g 5 :: ([Bool], StdGen)
([False,False,False,True,False],1648254783 2147483398)

请注意,随机数组是相同的。

虽然该功能遇到了拆分发电机的麻烦,但我们会立即丢弃它。相反,通过将g2线程化为后续调用来使用ghci> let (a1,g2) = randoms' g 5 :: ([Bool], StdGen) ghci> let (a2,_) = randoms' g2 5 :: ([Bool], StdGen) ghci> (a1,a2) ([False,False,False,True,False],[True,True,True,False,True] ,如

IO

如果您的代码在myAction :: Int -> IO ([Float],[Float]) myAction n = do g <- getStdGen let (f1,g2) = randoms' g n let (f2,g3) = randoms' g2 n setStdGen g3 return (f1, f2) monad中运行,则可以使用setStdGen替换最后的全局随机数生成器,如

State

线程状态很尴尬且容易出错。如果您有许多重复的样板,请考虑使用ST或{{1}}。