Haskell随机数

时间:2016-05-28 09:02:41

标签: haskell

我正在尝试在Haskell中生成随机数的样本

import System.Random

getSample n = take n $ randoms g where
    g = newStdGen

但似乎我并没有以正确的方式使用newStdGen。我错过了什么?

1 个答案:

答案 0 :(得分:1)

首先,您可能不想使用newStdGen。最大的问题是,每次运行程序时都会得到不同的种子,因此没有结果可以重现。在我看来,mkStdGen是一个更好的选择,因为它需要你给它种子。这意味着每次都会获得相同的(伪)随机数序列。如果你想要一个不同的序列,只需改变种子。

newStdGen的第二个问题是,由于它不纯,你最终会进入IO monad,这可能有点不方便。

sample :: Int -> IO [Int]
sample n = do
  gen <- newStdGen
  return $ take n $ randoms gen

你可以使用do-notation来提取&#39;价值然后总结:

main :: IO ()
main = do
  xs <- sample 10
  s = sum xs
  print s

或者你可以&#39; fmap&#39;结果的函数(但请注意,在某些时候你可能需要提取值):

main :: IO ()
main = do
  s <- fmap sum $ sample 10
  print s

fmap函数是map的通用版本。就像map将函数应用于列表中的值一样,fmap可以将函数应用于IO内的值。

这个sample函数的另一个问题是,如果我们再次调用它,它将以新的种子开始,而不是继续前一个(伪)随机序列。同样,这使得复制结果变得不可能。为了解决这个问题,我们需要传入种子并返回一个新的种子。遗憾的是,randoms并未为我们返回下一个种子,因此我们必须使用random从头开始编写此内容。

sample :: Int -> StdGen -> ([Int],StdGen)
sample n seed1 = case n of
  0 -> ([],seed1)
  k -> let (rs,seed2) = sample (k-1) seed1
           (r, seed3) = random seed2
        in ((r:rs),seed3)

我们的主要功能现在是

main :: IO ()
main = do
  let seed1 = mkStdGen 123456
      (xs,seed2) = sample 10 seed1
      s = sum xs
      (ys,seed3) = sample 10 seed2
      t = sum ys
  print s
  print t

我知道这对于使用随机数似乎是一项非常多的工作,但优点是值得的。我们可以使用单个种子生成所有随机性,以保证结果可以重现。

当然,这是Haskell,我们可以利用Monads来摆脱种子值的所有手动线程。这是一种稍微高级的方法,但值得学习,因为monads在Haskell代码中无处不在。

我们需要这些进口:

import System.Random
import Control.Monad
import Control.Applicative

然后我们将创建一个newtype,它表示将种子转换为值和下一个种子的动作。

newtype Rand a = Rand { runRand :: StdGen -> (a,StdGen) }

我们需要FunctorApplicative个实例或GHC会抱怨,但我们可以避免在此示例中实现它们。

instance Functor Rand
instance Applicative Rand

现在为Monad实例。这就是魔术发生的地方。 >>=函数(称为bind)是我们指定如何通过计算线程化种子值的地方。

instance Monad Rand where
  return x = Rand ( \seed -> (x,seed) )
  ra >>= f = Rand ( \s1 -> let (a,s2) = runRand ra s1
                           in  runRand (f a) s2 )

newRand :: Rand Int
newRand = Rand ( \seed -> random seed )

现在我们的sample功能非常简单!我们可以利用来自replicateM的{​​{1}}重复给定的操作并将结果累积到列表中。有种子价值的所有有趣的事业都在幕后处理

Control.Monad

如果我们需要多次生成随机值,我们甚至可以留在sample :: Int -> Rand [Int] sample n = replicateM n newRand main :: IO () main = do let seed1 = mkStdGen 124567 (xs,seed2) = runRand (sample 10) seed1 s = sum xs print s monad中。

Rand

我希望这有帮助!