生成无限的RandomGen列表

时间:2014-01-03 13:29:28

标签: list haskell random infinite

我想在列表中应用需要RandomGen的函数f。我试着生成一个无限的RandomGen列表,如下所示。 (只是由函数“randoms”生成的随机值是不够的,因为所需的值范围取决于f的输入。)

module Test where
import System.Random (getStdGen, RandomGen, randomR, random)

f :: (RandomGen g, Integral a) => g -> a -> Bool

randomGens :: RandomGen g => g -> [g]
randomGens gen =
  let (i, gen') = (random gen) :: (Int, g1)
  in  gen : (repeatGen gen')

不幸的是,编译器告诉我,它失败了

Test.hs:13:26:
    Could not deduce (g1 ~ g)
    from the context (RandomGen g)
      bound by the type signature for
             randomGens :: RandomGen g => g -> (g, g)
      at Test.hs:11:14-39
    or from (RandomGen g1)
      bound by an expression type signature: RandomGen g1 => (Int, g1)
      at Test.hs:13:19-55
      `g1' is a rigid type variable bound by
           an expression type signature: RandomGen g1 => (Int, g1)
           at Test.hs:13:19
      `g' is a rigid type variable bound by
      the type signature for randomGens :: RandomGen g => g -> (g, g)
          at Test.hs:11:14
    In the first argument of `random', namely `gen'
    In the expression: random gen :: RandomGen g => (Int, g)
    In a pattern binding:
      (i, gen') = random gen :: RandomGen g => (Int, g)

只是在let-binding中跳过类型注释(Int,g1)不起作用。他需要具有“随机”

应用的结果类型

3 个答案:

答案 0 :(得分:4)

忽略生成无限生成器的无限列表是否真的可行,System.Random中存在一个名为split的函数,可用于创建新生成器而不是调用虚拟类型上的random并抛弃生成的值。使用split,您可以像这样实施randomGens

import Data.List (unfoldr)
import System.Random

randomGens :: RandomGen g => g -> [g]
randomGens = unfoldr (Just . split)

但是,您应该只使用randomRs,它会在给定范围内生成无限的值流。

答案 1 :(得分:2)

快速回答

您可以将您想要的功能定义为

randomGens :: (RandomGen g) => g -> [g]
randomGens g = let (g0,g1) = split g in g0 : randomGens g1

答案稍长

以上可能不是应用需要随机性到列表的函数的最佳方法。我可以定义一个辅助函数来做到这一点

mapRandom :: (RandomGen g) => (g -> a -> b) -> g -> [a] -> (g, [b])
mapRandom _ g []     = (g, [])
mapRandom f g (a:as) = let (_,g1) = next g
                        in f g a : mapRandom f g1 as

然后你可以写

>> g <- newStdGen
>> mapRandom f g [1..5]
([False,False,True,False,True], 1839473859 293842934)

最佳答案

函数mapRandom看起来非常混乱。那是因为我们不得不搞乱手动更新发电机的细节。幸运的是,你不必那样做!包Control.Monad.Random为您提供了很好的组合,几乎完全抽象出了生成器的概念。说你目前有

f :: (RandomGen g) => g -> Int -> Bool
f g n = let (x,_) = random g in x < n

我会重写那个

f :: (RandomGen g) => Int -> Rand g Bool
f n = do
  x <- getRandom
  return (x < n)

并使用mapM将此功能映射到列表中。您可以使用

运行它
>> gen <- newStdGen
>> runRand (mapM f [1..10]) gen
([False,True,True,False,True], 1838593757 1838473759)

其中第一个元素是将随机函数映射到列表上的结果,最后一个元素是生成器的当前值。请注意,在定义f时,您根本不必担心生成器--Haskell会负责更新生成器并在后台生成新的随机数。

答案 2 :(得分:1)

这里的主要问题 - 编译器无法理解等式g1g(列表总是同态的!)

需要使用ScopedTypeVariables扩展名,如下所示:

{-# LANGUAGE ScopedTypeVariables #-}

randomGens :: forall g. RandomGen g => g -> [g]
randomGens gen =
  let (i, gen') = (random gen) :: RandomGen g => (Int, g)
  in  gen : (randomGens gen')

我们添加forall g指向g的范围上下文。

正如克里斯·泰勒所提到的,这个功能无效,需要计算两次随机数 - 第一次计算新g,第二次计算新的随机数。

因此,使用MonadRand在州内保存新的生成器数字要好得多。

<强>已更新

对于简单的情况,我们可以使用zipWith

randomsMap :: (RandomGen g, Random a) => g -> (a -> b -> c) -> [b] -> [c]
randomsMap g f xs = zipWith f (randoms g) xs