Algorithm DPLL算法和访问节点数

Algorithm DPLL算法和访问节点数,algorithm,haskell,recursion,artificial-intelligence,dpll,Algorithm,Haskell,Recursion,Artificial Intelligence,Dpll,我正在实现计算访问节点数的DPLL算法。我设法实现了不计算访问节点数的DPLL,但我想不出任何解决计数问题的方法。主要的问题是,当算法找到满意的估值并返回True时,递归将向上滚动,并从递归开始的那一刻起返回计数器。在任何命令式语言中,我只会使用全局变量,并在调用函数时立即递增它,但在Haskell中不是这样 我粘贴在这里的代码并不代表我试图解决计数问题,它只是我没有它的解决方案。我尝试使用元组,如True、Int,但它总是从递归开始时返回整数值 这是我的实现,其中节点->变量是一个启发式函数,

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

我粘贴在这里的代码并不代表我试图解决计数问题,它只是我没有它的解决方案。我尝试使用元组,如True、Int,但它总是从递归开始时返回整数值

这是我的实现,其中节点->变量是一个启发式函数,句子是CNF中要满足的子句列表,[变量]是一个未分配的文本列表,模型只是一个真值评估

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

编辑:

我尝试实现的一个可能的解决方案是使用tuple 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中是否可行。

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

这可以进一步使用。这是一个关于haskell和state monad的非常好的教程。基本上,a->b到a->Int->b,Int的转换可以被看作是a->b到a->State intb的转换,只需给函数Int->b,Int起一个更好的名字。有一个非常好的例子可以很容易地解释这些好的抽象从何而来

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'

您可以使用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)
这是状态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而不是计数器,返回1而不是计数器+1-并且对函数的调用会更简单,只需少一个参数就可以正确设置。

我还不确定我是否理解这个问题。从递归开始的那一刻起,它总是返回整数值意味着什么?在一个可能的解决方案中,我使用True,Int tuple来返回节点计数器。当DPLL函数到达OR语句时,在应用递归n次直到模型满足语句后,函数在初始OR递归开始的同一级别上计算OR语句,最终从该特定递归级别返回计数器。我希望你现在能理解这个问题。谢谢。那么,您是在问如何合并在RecurusingEuristicFunction的v:vs情况下对dpll'的两个调用返回的两个INT吗?如果是,为什么+不是合并它们的正确方法?如果您认为使用tuple Bool,Int可以解决问题,那么是的。在我试图解决它的过程中,我基本上无法从OR语句中较低级别的递归中检索计数器值。您应该包括一个MWE:显示您不喜欢的行为的最小完整代码片段,以及对如何运行它的描述,以及您期望发生的情况,第二个解决方案非常有趣,但不幸的是,我只允许使用基本的库。请参见更新的问题,很抱歉造成混淆。你能给我举个例子说明第一个解决方案是如何工作的吗?感谢您将纯函数从a->…->将b转换为函数a->…->Int->b,Int像fa=表达式变成fac=表达式,如果。。。然后是c+1,否则是c,当您需要将新状态传递给某个被调用方时,您将需要提取新状态。在某些情况下,您希望函数返回Bool,Int,其中Int是当前状态:然而,如果你的状态只沿着一条递归路径改变,那么你可以简单地用一个表示当前计数的参数来扩展你的函数,并在递归的基本情况下将该数字添加到输出中。我认为这在某种意义上类似于我尝试的
ng要查看第二个代码段。如果您能看看我的“解决方案”,并告诉我它有什么问题,我将不胜感激。谢谢它起作用了!伟大的非常感谢。然而,它增加了巨大的开销。不带计数器的版本在4s中解决了我的一个测试问题,而带计数器的版本在22秒内解决了完全相同的问题。可能有什么问题?@jdet我的盲点是在模式中添加~或使用let而不是case,比如case dpll。。。对于~结果,计数器->。。。。第二种尝试是,当它是一个论点时,更小心地强制计数器。但是,一个真正经过实验测试的答案,而不是即兴回答,需要您花时间将其转化为一个真正的问题。我认为,现在DPLL可能必须评估OR语句的两面,而当lhs评估为真时,它可以在之前的实现中提前终止。我会试着自己解决它。不过,谢谢你的帮助,丹尼尔。我真的很感激。如果这句话能解决问题,那就简单了。再次感谢丹尼尔!