【问题标题】:DPLL algorithm and number of visited nodesDPLL算法和访问节点数
【发布时间】:2017-08-03 23:30:10
【问题描述】:

我正在实施 DPLL 算法来计算访问节点的数量。我设法实现了不计算访问节点的 DPLL,但我想不出任何解决计数问题的方法。主要问题是,当算法找到令人满意的估值并返回 True 时,递归从递归开始的那一刻起就向上滚动并返回计数器。在任何命令式语言中,我都会使用全局变量并在调用函数时立即递增它,但在 Haskell 中并非如此。

我在这里粘贴的代码并不代表我尝试解决计数问题,这只是我没有它的解决方案。我尝试使用 (True,Int) 之类的元组,但它总是从递归开始的那一刻起返回整数值。

这是我的实现,其中 (Node -> Variable) 是启发式函数,Sentence 是 CNF 中要满足的子句列表,[Variable] 是未分配的 Literals 列表,Model 只是一个真值评估。

dpll' :: (Node -> Variable) -> Sentence -> [Variable] -> Model -> Bool
dpll' heurFun sentence vars model
  | satisfiesSentence model sentence  = True
  | falsifiesSentence model sentence  = False
  | otherwise         = applyRecursion
    where
      applyRecursion
        | pureSymbol /= Nothing = recurOnPureSymbol
        | unitSymbol /= Nothing = recurOnUnitSymbol
        | otherwise             = recurUsingHeuristicFunction
          where
            pureSymbol  = findPureSymbol vars sentence model
            unitSymbol  = findUnitClause sentence model
            heurVar = heurFun (sentence,(vars,model))
            recurOnPureSymbol =
              dpll' heurFun sentence (vars \\ [getVar pureSymbol]) ((formAssignment pureSymbol):model)
            recurOnUnitSymbol =
              dpll' heurFun sentence (vars \\ [getVar unitSymbol]) ((formAssignment unitSymbol):model)
            recurUsingHeuristicFunction = case vars of
              (v:vs) ->     (dpll' heurFun sentence (vars \\ [heurVar]) ((AS (heurVar,True)):model)
                        ||   dpll' heurFun sentence (vars \\ [heurVar]) ((AS (heurVar,False)):model))
              []     ->     False

我非常感谢有关如何计算已访问节点的任何建议。谢谢。

编辑:

我可以使用的唯一库是 System.Random、Data.Maybe 和 Data.List。

编辑:

我尝试实现的一种可能的解决方案是使用元组 (Bool,Int) 作为 DPPL 函数的返回值。代码如下:

dpll'' :: (Node -> Variable) -> Sentence -> [Variable] -> Model -> Int -> (Bool,Int)
dpll'' heurFun sentence vars model counter
  | satisfiesSentence model sentence  = (True,counter)
  | falsifiesSentence model sentence  = (False,counter)
  | otherwise         = applyRecursion
  where
    applyRecursion
      | pureSymbol /= Nothing = recurOnPureSymbol
      | unitSymbol /= Nothing = recurOnUnitSymbol
      | otherwise             = recurUsingHeuristicFunction
      where
        pureSymbol  = findPureSymbol vars sentence model
        unitSymbol  = findUnitClause sentence model
        heurVar = heurFun (sentence,(vars,model))
        recurOnPureSymbol =
          dpll'' heurFun sentence (vars \\ [getVar pureSymbol]) ((formAssignment pureSymbol):model) (counter + 1)
        recurOnUnitSymbol =
          dpll'' heurFun sentence (vars \\ [getVar unitSymbol]) ((formAssignment unitSymbol):model) (counter + 1)
        recurUsingHeuristicFunction = case vars of
          (v:vs)    ->    ((fst $ dpll'' heurFun sentence (vars \\ [heurVar]) ((AS (heurVar,True)):model) (counter + 1))
                      ||  (fst $ dpll'' heurFun sentence (vars \\ [heurVar]) ((AS (heurVar,False)):model) (counter + 1)),counter)
          []        -> (False,counter)

这种方法的基本思想是在每次递归调用时递增计数器。但是,这种方法的问题是我不知道如何从 OR 语句中的递归调用中检索计数器。我什至不确定这在 Haskell 中是否可行。

【问题讨论】:

  • 我还不确定我是否理解问题所在。 “从递归开始的那一刻起,它总是返回整数值”是什么意思?
  • 在一种可能的解决方案中,我使用 (True,Int) 元组返回节点计数器。当 DPLL 函数到达 OR 语句并且在应用递归 n 次直到模型满足 Sentence 之后,函数在与初始 ORed 递归开始的相同级别评估 OR 语句,最终从该特定递归级别返回计数器。我希望你现在能理解这个问题。谢谢。
  • 那么,您是在问如何在v:vsrecurUsingHeuristicFunction 的情况下合并从对dpll' 的两次调用返回的两个Ints?如果是这样,为什么(+) 不是合并它们的正确方法?
  • 如果您认为使用元组 (Bool,Int) 可以解决问题,那么可以。在我尝试解决它时,我基本上无法从 OR 语句中较低级别的递归中检索计数器值。
  • 你应该包括一个 MWE:最小的完整的代码,展示你不喜欢的行为,以及如何运行它的描述,你期望发生什么,以及什么而是发生了。

标签: algorithm haskell recursion artificial-intelligence dpll


【解决方案1】:

您可以使用case 或类似方法从递归调用中检索计数器。

recurUsingHeuristicFunction = case vars of
    v:vs -> case dpll'' heurFun sentence (vars \\ [heurVar]) (AS (heurVar,True):model) (counter + 1) of
        (result, counter') -> case dpll'' heurFun sentence (vars \\ [heurVar]) (AS (heurVar,False):model) counter' of
            (result', counter'') -> (result || result', counter'')
    []   -> (False,counter)

这是State monad 的手动实现。但是,我不清楚你为什么要经过柜台。就退货吧。然后是更简单的Writer monad。该助手的代码如下所示:

recurUsingHeuristicFunction = case vars of
    v:vs -> case dpll'' heurFun sentence (vars \\ [heurVar]) (AS (heurVar,True):model) of
        (result, counter) -> case dpll'' heurFun sentence (vars \\ [heurVar]) (AS (heurVar,False):model) of
            (result', counter') -> (result || result', counter + counter' + 1)
    []   -> (False,0)

其他结果会类似——返回0而不是counter1而不是counter+1——并且对函数的调用会更简单,只需少一个参数来担心正确设置.

【讨论】:

  • 有效!伟大的。谢谢你。但是,它增加了巨大的开销。没有计数器的版本在 4 秒内解决了我的一个测试问题,而有计数器的版本在 22 秒内解决了完全相同的问题。可能是什么问题?
  • @jdet 我的盲点是将~ 添加到模式中(或使用let 而不是case),如case dpll'' ... of ~(result, counter) -> ...。第二次尝试是在争论时更仔细地强制counter。但是,真正的(经过实验测试,而不是即兴发挥)答案需要您花时间将其变成一个真正的问题。
  • 我认为现在 DPLL 可能必须评估 OR 语句的两侧,而一旦 lhs 评估为 True,它可能会在先前的实现中提前终止。我会尝试自己解决。但是,感谢您的帮助丹尼尔。我真的很感激。
  • 简单的 if 语句解决了问题。再次感谢丹尼尔!
【解决方案2】:

基本上你用命令式语言描述的解决方案可以通过传递一个计数变量来建模,在你返回它时将变量添加到结果中(达到可满足赋值的递归底部),即对于一个函数a -> b 你将创建一个新函数a -> Int -> (b, Int)Int 参数是计数器的当前状态,结果会随着计数器的更新状态而丰富。

这可以使用state monad 进一步优雅地重新表达。关于haskell 和状态monad 的一个非常好的教程是here。基本上a -> ba -> Int -> (b, Int) 的转换可以看作是a -> ba -> State Int b 的转换,只需给函数Int -> (b, Int) 起一个更好的名称。有一个非常好的blog,它以一种非常容易理解的方式解释了这些好的抽象来自哪里。

import Control.Monad.Trans.StateT

type Count = Int

dpllM :: (Node -> Variable) -> Sentence -> [Variable] -> Model -> State Count Bool
dpllM heurFun sentence vars model | ... = do
    -- do your thing
    modify (+1)
    -- do your thing

dpll' :: (Node -> Variable) -> Sentence -> [Variable] -> Model -> Bool
dpll' heurFun sentence vars model = runState (dpllM heurFun sentence vars model) 0

也许你想要类似的东西

f :: A -> Int -> (Bool, Int)
f a c =
    let a' = ...
        a'' = ...
        (b', c') = f a' c in f a'' c'

【讨论】:

  • 第二个解决方案真的很有趣,但不幸的是我只能使用基本库(请参阅更新的问题,抱歉造成混淆)。你能给我举个例子说明第一个解决方案是如何工作的吗?谢谢
  • 只要把你的纯函数从a -> ... -> b变成一个函数a -> ... -> Int -> (b, Int)就像f a = expression变成f a c = (expression, if ... then c + 1 else c)然后你需要提取新的状态,当你需要将它传递给某个被调用者时.在某些时候,您会希望您的函数返回(Bool, Int),其中Int 是当前状态。 :) 但是,如果您的状态仅沿一条递归路径更改,那么您可以简单地使用表示当前计数的参数扩展您的函数,并将该数字添加到递归基本情况的输出中。
  • 我认为这在某种意义上类似于我试图做的事情(参见第二个代码 sn-p)。如果您能看看我的“解决方案”并以某种方式告诉我它有什么问题,我将不胜感激。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-14
相关资源
最近更新 更多