如何在Prolog中将命题公式转换为析取范式(DNF)?

如何在Prolog中将命题公式转换为析取范式(DNF)?,prolog,Prolog,我是Prolog新手,我需要将结果从真值表转换为析取范式。 我已经能够生成如下所示的真值表: ?- table(p or(q and not r) or not s or r). [p,q,r,s]|(p或(q和非r)或非s或r)-------------------------------------------------------------[0,0,0,0]|1 |[0,0,0,1]|[0,0,1,0]|[0,0,1,1]|[0,1,0,0]|1 |[0,1,0,1]|[0,1,1,

我是Prolog新手,我需要将结果从真值表转换为析取范式。 我已经能够生成如下所示的真值表:

?- table(p or(q and not r) or not s or r).
[p,q,r,s]|(p或(q和非r)或非s或r)-------------------------------------------------------------[0,0,0,0]|1 |[0,0,0,1]|[0,0,1,0]|[0,0,1,1]|[0,1,0,0]|1 |[0,1,0,1]|[0,1,1,0]|[0,1,1,1]|[1,0,0,0,0]|[1,0,0,1]|[1,0,0,1]|[1,0,1,0]|[1,0,1,1]|1 |[1,1,0,0]| 1 |[1,1,0,1]|[1,1,1,0]|[1,1,1,1]|-------------------------------------
如果有人能帮我把这个表转换成析取范式,我会很感激的。

让我们实现一个通用真值表求值器,在中转换成Prolog可求值公式,然后,通过,我们将每个minterm分离:

:- op(900, fy, neg).
:- op(1000, xfy, and).
:- op(1100, xfy, or).

formula(p or (q and neg r) or neg s or r).

cnf(F, CNF) :-
    setof(V, literal(F, V), Ls),
    setof(La, T^(assign(Ls, La), translate(F, La, T), T), CNF).

literal((X or Y), L) :- literal(X,L) ; literal(Y,L).
literal((X and Y), L) :- literal(X,L) ; literal(Y,L).
literal(neg X, L) :- literal(X,L).
literal(L, L) :- atom(L).

assign(Ls, La) :- maplist(assign_literal, Ls, La).
assign_literal(L, L=true).
assign_literal(L, L=false).

translate((X or Y), Ls, (A;B)) :- translate(X, Ls, A), translate(Y, Ls, B).
translate((X and Y), Ls, (A,B)) :- translate(X, Ls, A), translate(Y, Ls, B).
translate(neg X, Ls, \+ A) :- translate(X, Ls, A).
translate(L, Ls, V) :- memberchk(L=V, Ls).
收益率:

?- formula(F),cnf(F,CNF),maplist(writeln,CNF).
[p=false,q=false,r=false,s=false]
[p=false,q=false,r=true,s=false]
[p=false,q=false,r=true,s=true]
[p=false,q=true,r=false,s=false]
[p=false,q=true,r=false,s=true]
[p=false,q=true,r=true,s=false]
[p=false,q=true,r=true,s=true]
[p=true,q=false,r=false,s=false]
[p=true,q=false,r=false,s=true]
[p=true,q=false,r=true,s=false]
[p=true,q=false,r=true,s=true]
[p=true,q=true,r=false,s=false]
[p=true,q=true,r=false,s=true]
[p=true,q=true,r=true,s=false]
[p=true,q=true,r=true,s=true]
F = or(p, or(and(q, neg(r)), or(neg(s), r))),
CNF = [[p=false, q=false, r=false, s=false], [p=false, q=false, r=true, s=false], [p=false, q=false, r=true, s=true], [p=false, q=true, r=false, s=false], [p=false, q=true, r=false, ... = ...], [p=false, q=true, ... = ...|...], [p=false, ... = ...|...], [... = ...|...], [...|...]|...].
抱歉,输出有点冗长。可根据进一步规格轻松定制

我使用了neg/1而不是/1(这已经是一个有效的Prolog操作符),只是为了明确区别

编辑

这里是一个简化,结果是一个语法概括。只更改了literal/2和translate/3,并添加了translate/2:

literal(F, L) :- F =.. [_,X,Y], (literal(X,L) ; literal(Y,L)).
literal(F, L) :- F =.. [_,X], literal(X,L).
literal(L, L) :- atom(L).

translate(and, (,)).
translate(or, (;)).
translate(neg, (\+)).

translate(F, Ls, T) :-
    F =.. [S,X,Y],
    translate(S,O),
    T =.. [O,A,B],
    translate(X, Ls, A), translate(Y, Ls, B).
translate(F, Ls, T) :-
    F =.. [S,X],
    translate(S,O),
    T =.. [O,A],
    translate(X, Ls, A).
translate(F, Ls, T) :- memberchk(F=T, Ls).
更多编辑

上面的代码可以更加高效,只需将翻译移出循环即可

cnf(F, CNF) :-
    setof(V, literal(F, V), Ls),
    translate(F, La, T),
    setof(La, (assign(Ls, La), T), CNF).
最后一个translate/3子句中需要做一个小修改:使用member/2而不是memberchk

...
translate(F, Ls, T) :- member(F=T, Ls).
时间:与旧版本

4 ?- formula(F),time(cnf(F,CNF)).
% 1,788 inferences, 0.002 CPU in 0.002 seconds (98% CPU, 834727 Lips)
对于新的:

5 ?- formula(F),time(cnf(F,CNF)).
% 282 inferences, 0.001 CPU in 0.001 seconds (99% CPU, 315768 Lips)
大约好6倍

带memberchk的旧版本:

6 ?- formula(F),time(cnf(F,CNF)).
% 1,083 inferences, 0.002 CPU in 0.002 seconds (98% CPU, 561426 Lips)
好吧,还是差不多好4倍

编辑需要更多步骤才能获得真正的Prolog公式

cdnf(F, CNDF, Prolog) :-
    cdnf(F, CNDF),  % well, was cnf/2, I renamed to be more precise
    maplist(cj, CNDF, CJs),
    reverse(CJs, [H|T]),
    foldl(dj, T, H, Prolog).

dj(A, B, (A;B)).

cj(A, J) :-
    maplist(tf, A, B),
    reverse(B, [H|T]),
    foldl(cj, T, H, J).
cj(A, B, (A,B)).

tf(S=true,S).
tf(S=false,\+S).
现在,结果更有用了

?- formula(_,F), cdnf(F,_,P).
F = or(p, or(and(q, neg(r)), or(neg(s), r))),
P = (\+p, \+q, \+r, \+s;\+p, \+q, r, \+s;\+p, \+q, r, s;\+p, q, \+r, \+s;\+p, q, \+r, s;\+p, q, r, \+ ...;\+p, q, ..., ...;p, ..., ...;..., ...;...;...) 

这并不容易,因为第一列中的所有值都等于0。为什么所有行都显示[0,0,0,0]?表/1应在符号列表下显示1/0。
?- formula(_,F), cdnf(F,_,P).
F = or(p, or(and(q, neg(r)), or(neg(s), r))),
P = (\+p, \+q, \+r, \+s;\+p, \+q, r, \+s;\+p, \+q, r, s;\+p, q, \+r, \+s;\+p, q, \+r, s;\+p, q, r, \+ ...;\+p, q, ..., ...;p, ..., ...;..., ...;...;...)