Haskell如何生成无穷多的无限随机数列表用于模拟?
Haskell how to generate infinite number of lists of infinite random numbers for simulation?
在模拟问题中,我需要生成数千个行为彼此独立的代理对象。为此,我需要向这些代理中的每一个传递一个不同的随机数生成器。如何在 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>>
推测您有一些包含代理参数的结构和一个函数
agentRun :: AgentParams -> [Int] -> Agent
您的问题是,如何为每个代理创建无限的 Int 列表。
假设您从一个 AgentParam 列表开始,并且您想要映射 agentRun
到它上面,如下所示:
agentsParams :: [AgentParams]
agents = map (\p -> agentRun p ????) agentParams
问题是,您看不到如何将不同的无限列表放入每个代理中。
为此,您可以使用库函数 unfoldr
和 split
:
unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
split :: (RandomGen g) => g -> (g,g)
unfoldr
继续应用其功能,直到 returns Nothing
。
split
使用一个随机生成器和 returns 两个新的。所以 Just . split
为 unfoldr
提供了一个很好的论据。
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
但更好的解决方案是使用某种随机状态 monad 来为您管理所有这些。
在模拟问题中,我需要生成数千个行为彼此独立的代理对象。为此,我需要向这些代理中的每一个传递一个不同的随机数生成器。如何在 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>>
推测您有一些包含代理参数的结构和一个函数
agentRun :: AgentParams -> [Int] -> Agent
您的问题是,如何为每个代理创建无限的 Int 列表。
假设您从一个 AgentParam 列表开始,并且您想要映射 agentRun
到它上面,如下所示:
agentsParams :: [AgentParams]
agents = map (\p -> agentRun p ????) agentParams
问题是,您看不到如何将不同的无限列表放入每个代理中。
为此,您可以使用库函数 unfoldr
和 split
:
unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
split :: (RandomGen g) => g -> (g,g)
unfoldr
继续应用其功能,直到 returns Nothing
。
split
使用一个随机生成器和 returns 两个新的。所以 Just . split
为 unfoldr
提供了一个很好的论据。
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
但更好的解决方案是使用某种随机状态 monad 来为您管理所有这些。