【问题标题】:How can non-determinism be modeled with a List monad?非确定性如何用 List monad 建模?
【发布时间】:2014-01-05 11:52:42
【问题描述】:

谁能解释(用简单的英语例子更好)列表单子可以做什么来模拟非确定性计算?即问题是什么以及列表单子可以提供什么解决方案。

【问题讨论】:

  • 这里有很好的答案。添加一些我认为sequence 就是一个很好的例子。例如,sequence ["cbs","ae","tb"] 会为您提供从每个元素中选择一个字母来组成单词的所有方法。

标签: haskell f# functional-programming monads non-deterministic


【解决方案1】:

当我们说它是非确定性时,它意味着它有多个值。

Learn You A Haskell 这本书很好地解释了这一点:

像 5 这样的值是确定性的。它只有一个结果,我们知道 究竟是什么。另一方面,像 [3,8,9] 这样的值包含 多个结果,因此我们可以将其视为一个实际上是多个的值 同时价值观。使用列表作为应用函子展示 这种不确定性很好:

ghci> (*) <$> [1,2,3] <*> [10,100,1000]  
[10,100,1000,20,200,2000,30,300,3000]

从左到右相乘的所有可能组合 具有来自右列表的元素的列表包含在结果中 列表。在处理非确定性时,有很多选择 我们可以制作,所以我们只是尝试所有这些,结果是 非确定性的值也是如此,只是它有更多的结果。

很好地列出 monad 模型的非确定性。它的实例是这样的:

instance Monad [] where  
    return x = [x]  
    xs >>= f = concat (map f xs)  
    fail _ = [] 

因此,当您输入一个非确定性值时,它将产生另一组非确定性值:

ghci> [3,4,5] >>= \x -> [x, x * 2]
[3,6,4,8,5,10]

【讨论】:

    【解决方案2】:

    这是一个基于抛硬币的示例。问题如下:

    您有两个硬币,分别标记为 BiasedFairBiased 硬币有两个正面,Fair 硬币有一个正面和一个反面。随机选择其中一枚硬币,将其抛掷并观察结果。如果结果是正面,您选择偏向硬币的概率是多少?

    我们可以在 Haskell 中进行如下建模。首先,您需要硬币的类型和它们的面孔

    data CoinType = Fair | Biased deriving (Show)
    
    data Coin = Head | Tail deriving (Eq,Show)
    

    我们知道投掷公平硬币可能会出现HeadTail,而有偏见的硬币总是出现Head。我们使用一系列可能的替代方案对此进行建模(隐含地,每种可能性都具有相同的可能性)。

    toss Fair   = [Head, Tail]
    toss Biased = [Head, Head]
    

    我们还需要一个随机挑选公平或有偏见硬币的函数

    pick = [Fair, Biased]
    

    然后我们像这样把它们放在一起

    experiment = do
      coin   <- pick         -- Pick a coin at random
      result <- toss coin    -- Toss it, to get a result
      guard (result == Head) -- We only care about results that come up Heads
      return coin            -- Return which coin was used in this case
    

    请注意,虽然代码看起来像是我们只是运行了一次实验,但 list monad 正在建模非确定性,并且实际上遵循所有可能的路径。因此结果是

    >> experiment
    [Biased, Biased, Fair]
    

    因为所有可能性的可能性都相同,我们可以得出结论,我们有 2/3 的机会得到有偏见的硬币,只有 1/3 的机会得到公平的硬币。

    【讨论】:

    • 下一个来这里的人可以留言说你来自哪里吗?我很想知道为什么一年后这个答案突然开始获得大量支持!
    • 我正在 Haskell 上上 edx 的课程,并尝试了解 monad。我是通过谷歌搜索被带到这里的。
    • @ChrisTaylor 最近从这里链接到:stackoverflow.com/q/29886852/1463507。还是不错的答案!
    • 这与 PROMELA 中的模型检查有何可比性?当有很多状态时,它会像 SPIN 一样高效吗? GHC 会以此为基础构建 Büchi 自动机吗?
    • @JanusTroelsen GHC 将构建一个列表,其中包含所有可能的输出,包括重复输出。由于这个原因,它通常效率很低(例如,它无法将具有相同结果的多条路径折叠成一条路径)。有一些基于集合或关联数组(映射)的分布建模方法可以有效地折叠等效状态,但您通常需要非常小心,以免意外爆炸状态空间。目前,在这项任务上,面向概率的语言比 Haskell 等通用语言具有优势。
    【解决方案3】:

    list monad 可以表示“来自非确定性计算的所有可能结果”。比如函数

    f x = [x, x + 1, x + 2]
    

    可以解释为采用x 并返回xx+1x+2 之一的非确定性计算。

    功能

    g x = [2 * x, 3 * x]
    

    可以解释为采用x 并返回2 * x3 * x 的非确定性计算。这两个非确定性计算的“组合”应该是另一个非确定性计算,它采用x,将其转换为xx + 1x + 2 之一,然后将其加倍或加倍.因此,就列表而言,结果应该是所有六种可能性的列表

    现在

    g =<< f x = [2 * x, 3 * x, 2 * (x + 1), 3 * (x + 1), 2 * (x + 2), 3 * (x + 2)]
    

    因此,这确实像我们预期的那样模拟了非确定性。

    (将列表用于非确定性有一些尴尬,因为它们也有元素的顺序。“set monad”可能是模拟非确定性的更自然的方式。列表当然包含足够的 信息来模拟非确定性,但排序意味着我们有 更多 信息而不是必要的。)

    编辑:事实上,我所写的只是使用列表应用实例。为了获得充分利用单子接口的东西,您需要一个返回许多取决于其输入的结果的计算,例如

    g 0 = [1000, 1001]
    g x = [2 * x, 3 * x, 4 * x]
    

    虽然这是一个完全武断且毫无动机的例子!

    【讨论】:

      【解决方案4】:

      因此,在这里明确定义“非确定性”的含义很重要,因为它与在非确定性算法中的理解方式并不完全相同。这里捕捉到的意义是计算分支 - 系统可以在任何特定点移动到多个状态。

      列表对此进行建模,因为简单地说,它们包含多个元素。更重要的是,一元推导为我们提供了一种组合非确定性结果的方法——即一次对所有分支进行建模。

      【讨论】:

        猜你喜欢
        • 2021-10-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-28
        • 1970-01-01
        • 2016-05-14
        相关资源
        最近更新 更多