【问题标题】:Why does this list contain more than one distinct value?为什么此列表包含多个不同的值?
【发布时间】:2015-04-30 05:48:15
【问题描述】:

我的代码如下所示:

import qualified Data.Vector as V
import System.Random
import Control.Monad.State.Lazy
nextWeightedRandom :: State (StdGen, V.Vector Int) Int
nextWeightedRandom = do
  (g, fs) <- get
  let (i, g') = randomR (0, V.length fs - 1) g
  put (g', fs)
  return (fs V.! i)

weightedRandomList :: (StdGen, V.Vector Int) -> [Int]
weightedRandomList = evalState $ mapM (\_ -> nextWeightedRandom) [1..]

QuickCheck 确认来自 weightedRandomList 的值不同并且大致具有我希望的分布(Vector Int 看起来像 [5, 5, 5, 10],因此 nextWeightedRandom 以 3/4 的概率吐出 5和 10 个概率为 1/4)。

我能够通过猜测类型来使其工作,但我很困惑 --- evalState 不是在同一生成器的一堆不同副本上运行吗?为什么生成的列表不像[5, 5, 5, 5,...]

【问题讨论】:

  • 你可能会喜欢randomRs
  • @DanielWagner 这不会根据给定的分布对其进行加权。另外,我对这个问题的主要困惑是State monad 的工作原理,而不是找到更好的方法来生成随机数。
  • 我明白你的困惑是什么,并且正在写一个关于它的答案。评论是评论,不是回答。至于给定的分布——好吧,randomR 也没有。您可以在randomRs 为您提供的无限列表上应用相同的转换,您可以对randomR 为您提供的元素应用相同的转换。例如weightedRandomList (g, fs) = map (fs V.!) (randomRs (0, V.length fs - 1) g).
  • @DanielWagner 知道了,谢谢!

标签: haskell random monads state-monad


【解决方案1】:

由于您在nextWeightedRandom 中调用put,因此每次迭代都会更新状态。所以每次调用randomR ... g,都是使用上一次迭代的生成器输出。

事实上,这个问题本身,“evalState 不是在同一个生成器的一堆不同副本上运行”是一个非常有趣的问题,因为 evalState 没有收到任何东西的很多副本!它有一个生成器,并从那里开始完成所有工作。

【讨论】:

  • 我无法理解这个问题——我知道mapM 会返回类似于[nextWeightedRandom, nextWeightedRandom, nextWeightedRandom,...] 的内容,所以我希望每次评估都返回相同的结果——我是错了吗?
  • @PatrickCollins mapM 不仅如此,它使用&gt;&gt;= 链接这些单子动作,State 单子定义。具体来说,您可以将每个nextWeigthedRandom 视为一个获取旧随机种子并返回一个新整数和一个新随机种子的函数。运算符&gt;&gt;= 只是连接种子。这样,对nextWeightedRandom 的每次调用都有不同的种子参数,并且可以返回不同的整数。
  • @chi 啊,好的。那么(:)&gt;&gt;= 在列表 monad 中吗?
  • @PatrickCollins 不,大致是 mapM: 替换为 &gt;&gt;=。此处不涉及列表单子:此 mapMState 单子中工作。
  • @PatrickCollins 不,: 不是列表的&gt;&gt;= 的实现。此外,“由: 运算符连接在一起的一堆值”是对 any 列表的描述,无论它是如何计算的;这应该会让您对将该属性归因于 mapM 的使用感到紧张。
猜你喜欢
  • 1970-01-01
  • 2013-05-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多