【问题标题】:Haskell how to generate infinite number of lists of infinite random numbers for simulation?Haskell如何生成无限数量的无限随机数列表进行模拟?
【发布时间】:2017-02-28 04:20:25
【问题描述】:

在模拟问题中,我需要生成数千个彼此独立行为的代理对象。为此,我需要为每个代理传递一个不同的随机数生成器。如何在 Haskell 中做到这一点? 在像 C 这样的语言中,我可以在需要时生成一个随机数,但在 Haskell 中,我只需要在 IO monad 中执行此操作。代理在完全纯计算中的行为。

我现在是这样做的:

import System.Random
main = do
   gen1 <- newStdGen
   let rands1 = randoms gen :: [Int]
   gen2 <- newStdGen
   let rands2 = randoms gen :: [Int]
   let sout = map (agentRun extraArgs) [rands1,rands2]
   writeFile "output" $ unlines sout

agentRun = <<some pure code that uses random numbers>>

下面给出了我可以解决这个问题的一种方法,但它的缺点是它只需要在 IO monad 中运行,而且我不能只将函数映射到列表上。有没有更好、更像 Haskell 的方法来做到这一点?如果是,请给出示例代码示例。

请注意,代理需要访问无限列表,因为事先不知道需要多少随机数。

import System.Random
main = do
   dorun 10
dorun 0 = return ()
dorun n = do
   gen1 <- newStdGen
   let rands1 = randoms gen :: [Int]
   let sout = agentRun extraArgs rands1
   appendFile "output" sout
   dorun (n-1)

agentRun = <<some pure code that uses random numbers>>

【问题讨论】:

  • 你的方法是完全正确的——让你的函数将一个无限列表作为参数,并在main中实际生成列表。此外,Int 的无限列表和Int 的无限列表包含相同数量的元素。您已经证明您没有无限数量的代理,那么为什么没有有限数量的随机列表:map (agentRun args . randoms) &lt;$&gt; replicateM numRuns newStdGen。最后,如果您经常处理随机性,您可能会受益于some abstraction
  • Data.List.unfoldr (Just . split) :: RandomGen b =&gt; b -&gt; [b] 给你一个无限的生成器列表,给定一个。如果您的函数有这样的约束,它可以使用无限列表的无限列表 Ints 。我不知道这是否不合理。
  • 请注意,System.Random 是一个非常糟糕的生成器,可能会导致您的程序行为不佳。考虑使用其他生成器之一,例如来自 tf-random 的System.Random.TF
  • 你可以让你的函数在 Rand monad 中工作...
  • @user2407038 我知道 Int 的无限列表和 Int 的无限列表的无限列表包含相同数量的元素。但问题是我不能轻松(有效地)将这些传递给不同的代理。

标签: haskell random simulation infinite


【解决方案1】:

大概你有一些包含代理参数的结构和一个函数

agentRun :: AgentParams -> [Int] -> Agent

您的问题是,如何为每个代理创建一个无限的 Int 列表。

假设您从一个 AgentParams 列表开始,并且您想在其上映射 agentRun,如下所示:

agentsParams :: [AgentParams]
agents = map (\p -> agentRun p ????) agentParams

问题是,您看不到如何为每个代理获取不同的无限列表。

为此,您需要使用库函数 unfoldrsplit

unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
split :: (RandomGen g) => g -> (g,g)

unfoldr 继续应用它的函数,直到它返回Nothing

split 采用随机生成器并返回两个新生成器。所以Just . splitunfoldr 提供了一个很好的论据。

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

您现在可以将这个无限的生成器列表映射到一个无限的无限列表中:

infiniteRandomLists :: (RandomGen g) => g -> [[Int]]
infininteRandomLists = map randoms . infiniteGenerators

然后你可以使用 zipWith 来获取你的代理列表:

agents gen = zipWith agentRun (infiniteRandomLists gen)

但是,在编写代理程序时,您可能会发现使用生成器比使用无限的 Int 列表更容易。这是因为此类函数中的一个常见模式,您可以在其中编写:

(gen2, gen3) = split gen1
result = foo gen2

这使您不必将更新的生成器从foo 传回。如果您要从无限列表中获取数字,则必须执行以下操作:

(randomList2, result) = foo randomList1

但更好的解决方案是使用某种随机状态单子来为您管理所有这些。

【讨论】:

  • 很好的答案。帮助我了解了有关生成器的重要知识。
  • Haskell 继续给我带来很多惊喜——所有惊喜。这个问题超出了我的范围,我想,没有更好的解决方案。但是split生成器的这个小技巧让我措手不及。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-17
相关资源
最近更新 更多