假设我具有以下布尔逻辑:
Z = (A or B) and (A or C)
是否有可能使用prolog(可能与某些库一起使用)找出Z为何为假并以以下格式返回答案:
再次询问Z = true时一切都相反。
还是这类问题不适合序言,我应该看一下SAT解算器或其他一些东西?
我的目标是分析程序的数据流并确定答案之类的问题。我希望这是对/错,但事实并非如此,
答案 0 :(得分:3)
我不得不说这是一个很酷的问题。
我看到了两种基本方法,一种是您使用Prolog的read_term/2
来获得一个术语以及该术语中使用的变量名,看起来像这样:
?- read_term(T, [variable_names(Names)]).
|: X and Y or Z and Q.
T = _5700 and _5702 or _5706 and _5708,
Names = ['X'=_5700, 'Y'=_5702, 'Z'=_5706, 'Q'=_5708].
给予一些思考,这似乎比给我带来的麻烦要大得多,所以我想我将说明一个“简单”版本,其中我们实际上没有变量,只有原子和一个单独的变量变量值列表。您可能无需大量工作就可以使下面的一个适应上面的一个,但这似乎并没有解决这个问题的必要。
首先,我们需要支持这些运营商:
:- op(500, yfx, or).
:- op(400, yfx, and).
我继续前进,分别给它们与+和*相同的优先级,这对我来说似乎很直观。
我将有一个变量和赋值列表,例如[x=true, y=false]
。我创建了一个谓词来进行管理,但是我认为最终会更好,因此将其删除,但是请注意,我们是在这里做出此决定的。
现在,我的计划是创建一个评估谓词,该谓词将采用表达式和变量值赋值。它会为提供的表达式产生一个布尔值,并产生一个“原因”。我将在此处作弊并使用“ is”运算符,但我这样做只是因为它对我来说很不错。最基本的术语是
evaluate(X, Assignments, Value, X is Value) :-
atom(X), memberchk(X=Value, Assignments).
因此我们现在可以支持类似x
的表达式:
?- evaluate(x, [x=true], V, R).
V = true,
R = (x is true).
?- evaluate(x, [x=false], V, R).
V = false,
R = (x is false).
这些有点像重言式;如果x = true,则表达式“ x”为true,因为x = true。假也是如此。但是我们在那里的原因是您所追求的。接下来,让我们处理“或”:
evaluate(X or _, Assignments, true, Reason) :-
evaluate(X, Assignments, true, Reason).
evaluate(_ or Y, Assignments, true, Reason) :-
evaluate(Y, Assignments, true, Reason).
因此,现在我们应该处理以下两种情况之一为真的情况:
?- evaluate(x or y, [x=true,y=false], V, R).
V = true,
R = (x is true) ;
false.
?- evaluate(x or y, [x=false,y=true], V, R).
V = true,
R = (y is true) ;
false.
如果给定x = true和y = true,我们将获得两种解决方案,一种用于x,另一种用于y。但是对于错误的情况,我们还需要一个规则:
evaluate(X or Y, Assignments, false, R1 and R2) :-
evaluate(X, Assignments, false, R1),
evaluate(Y, Assignments, false, R2).
因此,这里的想法是要注意“或”的两面均为假,然后将原因与“和”组合在一起。这样我们就可以得到:
?- evaluate(x or y, [x=false,y=false], V, R).
V = false,
R = (x is false)and(y is false).
由于我们委派了双方,现在我们可以看到大型而复杂的“或”序列应该起作用:
?- evaluate(a or b or c or d or e, [a=false,b=false,c=false,d=true,e=false], V, R).
V = true,
R = (d is true) ;
false.
您可以推断我们为“或”所做的工作是为“与”做的,这里我们基本上有两个错误的情况和一个真实的情况,而不是两个真实的情况和一个错误的情况:
evaluate(X and Y, Assignments, true, R1 and R2) :-
evaluate(X, Assignments, true, R1),
evaluate(Y, Assignments, true, R2).
evaluate(X and _, Assignments, false, Reason) :-
evaluate(X, Assignments, false, Reason).
evaluate(_ and Y, Assignments, false, Reason) :-
evaluate(Y, Assignments, false, Reason).
这适用于您可能期望的一些有趣的情况:
?- evaluate(x and y or z, [x=true, y=true, z=false], V, R).
V = true,
R = (x is true)and(y is true) ;
false.
?- evaluate(x and y or z, [x=false, y=false, z=true], V, R).
V = true,
R = (z is true) ;
false.
在某些情况下,这并不是超级有用,例如:
?- evaluate((a or b) and (a or c), [a=true,b=false,c=false], V, R).
V = true,
R = (a is true)and(a is true) ;
false.
如您所见,第一个解决方案并不是超级有用的信息,因为我们已经讲了a
;也许我们可以找到一种方法,通过更智能地组合孩子的答案来简化答案。另一方面,在这种情况下,它会有所帮助:
?- evaluate((a or b) and (a or c), [a=false,b=false,c=false], V, R).
V = false,
R = (a is false)and(b is false) ;
V = false,
R = (a is false)and(c is false).
?- evaluate((a or b) and (a or c), [a=false,b=true,c=true], V, R).
V = true,
R = (b is true)and(c is true) ;
false.
唯一需要更改以处理未定义值的部分是atom(X)
分支,应将其替换为:
evaluate(X, Assignments, Value, X is Value) :-
atom(X),
( memberchk(X=V, Assignments) ->
Value = V
; member(Value, [true,false])
).
当a=false
出现在绑定列表中时,将使用它。如果未出现,则会同时生成a=false
和a=true
。从完全通用的角度来看,这似乎涵盖了您的其他用例:
?- evaluate((a or b) and (a or c), [a=false], V, Reason).
V = true,
Reason = (b is true)and(c is true) ;
V = false,
Reason = (a is false)and(b is false) ;
V = false,
Reason = (a is false)and(c is false).
您可以将搜索限制为产生您感兴趣的值的案例:
?- evaluate((a or b) and (a or c), [a=false], false, Reason).
Reason = (a is false)and(b is false) ;
Reason = (a is false)and(c is false).
当然,Prolog在这里并没有做任何特别聪明的事情。它不是试图倒退找出b
和c
的值将导致错误的原因,它只是产生所有可能性并尝试将其评估与false
统一。因此,您添加到表达式中的每个未定义变量都会使搜索空间增加一倍。如果这是您担心的效率低下,那么这可能不是您的理想解决方案(或者可能很好,您可以尝试一下,看看它是否可以忍受)。
如果您最关心的是性能,我认为您可能希望研究SAT解算器,尽管我不知道这些解算器是否能够为您的推理提供“理由”。