优化Prolog程序(删除重复项,重复重新计算)

时间:2017-01-18 16:09:14

标签: prolog

我对Prolog非常缺乏经验。我有一个数据集,其中包含图形中具有圆度(非常多)的元素和关系。有规则来计算路径的汇总关系。其中之一是:一个采取路径,然后采取最弱的关系,那就是两端之间的关系。

使用 元素E1E2E3
关系R1/R1cR2R3(强度从低到高)和
结构E1-R3-E2E1-R1-E2E2-R2-E3E3-R1-E2

我可以做出以下最小例子:

% weaker table
isWeaker( r1, r2).
isWeaker( r2, r3).
weaker( X, Y) :- isWeaker( X, Y).
weaker( X, Y) :-
    isWeaker( X, Z),
    weaker( Z, Y).
% 'weakest' is <= not <
weakest( X, X, Y) :- =(X,Y).
weakest( X, X, Y) :- weaker( X, Y).

% All direct relations
isADirectRelation( e1, r1, e2).
isADirectRelation( e1, r3, e2).
isADirectRelation( e2, r2, e3).
isADirectRelation( e3, r1, e2).
isADirectRelation( e1, r3, e4).
isADirectRelation( e4, r2, e3).
isADirectRelation( e1, r1, e4).
isADirectRelation( e3, r1, e4).

% derived relations calculations

% Structural Chains
isADerivedRelation( Source, Relation, Target, Visited) :-
    \+ member( [Source,Relation,Target], Visited),
    weakest( Relation, RelationOne, RelationTwo),
    isARelation( Source, RelationOne, Intermediate, [[Source,Relation,Target]|Visited]),
    isARelation( Intermediate, RelationTwo, Target, [[Source,Relation,Target]|Visited]).

% major workhorse with anti-circularity
isARelation( Source, Relation, Target, Visited) :-
    (isADirectRelation( Source, Relation, Target);
     isADerivedRelation( Source, Relation, Target, Visited)).

isARelation( Source, Relation, Target, []).的结果是

e1,r1,e2
e1,r3,e2
e2,r2,e3
e3,r1,e2
e1,r3,e4
e4,r2,e3
e1,r1,e4
e3,r1,e4

e1,r1,e3
e3,r1,e3
e1,r1,e3 duplicate
e3,r1,e3 duplicate

缺少

e4,r1,e4
e2,r2,e2

是否有可能在Prolog中解决这个问题?正式,是的,当然,还有一个不错的表现?

2 个答案:

答案 0 :(得分:1)

关于这个问题,有很多事情要说,所以这将是一个漫长而漫无边际且最终不能令人满意的答案。所以我不妨从一个小小的开始:请不要使用camelCaseIdentifiers作为谓词,我们通常使用underscore_separated_words代替。我不确定为什么这会让我特别厌倦Prolog,但我怀疑是因为大写字母在语法上很重要。

继续,您的weakest/3谓词已被破坏:

?- weakest(Weakest, r2, r1).
false.

我认为您在问题的第一个版本中有这个权利,但之后您删除了weakest/3的第三个条款,因为您认为它导致了多余的答案。毋庸置疑,&#34;效率&#34;没有正确性就没用了。 (另外,我们通常将&#34;输出&#34;参数放在最后,而不是第一个。)

您获得冗余答案的部分原因是您使用isARelation/4isADerivedRelation/4的两次(间接)递归调用。你在计算什么就像是&#34;直接&#34;关系。在Prolog中表达传递闭包的常用方法是这样的:

transitive_closure(A, B) :-
    base_relation(A, B).
transitive_closure(A, C) :-
    base_relation(A, B),
    transitive_closure(B, C).

也就是说,我们首先采取基本步骤&#34;然后递归。如果我们的基本关系有成对a-bb-cc-d,那么这将找到解决方案a-d一次,作为基础对a-b的组合和派生的传递对b-d。相反,如果我们像你一样构造第二个子句,对transitive_closure/2进行两次递归调用,我们将得到两次解决方案a-d:一次如上,但也是一次,因为我们将得到及性对a-c并将其与c-d一起撰写,以提供a-d

您可以通过将isARelation/4中的第一个isADerivedRelation/4来电更改为isADirectRelation/3来电来解决此问题。

另一个问题是您使用Visited错误:在您证明这样的解决方案存在之前,您正在将对Source-Target标记为已访问过!您应该将Source-Intermediate标记为已访问。

即便如此,如果图中这些元素之间存在多条不同的路径,您仍将获得一对元素的冗余解决方案。这就是Prolog的逻辑运作方式:Prolog可以找到您查询的个​​别答案,但不允许您直接谈论这些答案之间的关系。如果我们想强制它一次枚举一切,我们必须留下纯粹的逻辑。

一些Prolog系统提供了一个名为&#34; tabling&#34;它基本上缓存了所有解决方案的问题。谓词并避免重新计算。这应避免多余的答案,甚至简化您的定义:如果您的闭包关系已被表格化,则您不再需要跟踪Visited列表,因为表格将避免循环重新计算。我无法给你测试代码,因为我没有Prolog,周围有桌子。即使没有你的系统提供的表格,也有理论上的可能性&#34; memoizing&#34;解决方案,使用Prolog的不纯数据库。没有多余的解决方案,很难完全正确地完成它。

作为不纯的Prolog的替代方案,您的问题似乎更适合。这些编程模型使用类似Prolog的语法,但设置语义似乎正是您想要的:一个命题或者是一个解决方案,没有冗余解决方案的概念。整个解决方案集一次计算。循环消除也是自动的,因此您不需要(事实上,由于受限制的输入语言,不能使用)Visited列表。如果我是你,我会尝试在Datalog中这样做。

作为Prolog的进一步扩展,可能存在基于约束处理规则(CHR)的漂亮解决方案。但实际上,请尝试Datalog。

最后,我不明白为什么你认为e2,r2,e2是一个缺失的解决方案。我看到的从e2e2的唯一路径经过e3并通过e2关系返回r1,这是最弱的关系,所以解决方案应为e2,r1,e2

答案 1 :(得分:0)

我最终得到了什么,多亏了Lurker的评论和Isabelle的回答:

% weaker table
isWeaker( r1, r2).
isWeaker( r2, r3).
weaker( X, Y) :- isWeaker( X, Y).
weaker( X, Y) :-
    isWeaker( X, Z),
    weaker( Z, Y).
% 'weakest' is <= not <
weakest( X, X, Y) :- =(X,Y).
weakest( X, X, Y) :- weaker( X, Y).

% All direct relations
isADirectRelation( e1, r1, e2).
isADirectRelation( e1, r3, e2).
isADirectRelation( e2, r2, e3).
isADirectRelation( e3, r1, e2).
isADirectRelation( e1, r3, e4).
isADirectRelation( e4, r2, e3).
isADirectRelation( e1, r1, e4).
isADirectRelation( e3, r1, e4).

% derived relations calculations

isARelation( Source, Relation, Target, _) :-
    isADirectRelation( Source, Relation, Target).

% Structural Chains
isARelation( Source, Relation, Target, Visited) :-
    \+ member( [Source,Relation,Target], Visited),
    weakest( Relation, RelationOne, RelationTwo),
    isADirectRelation( Source, RelationOne, Intermediate),
    isARelation( Intermediate, RelationTwo, Target, [[Source,RelationOne,Intermediate]|Visited]).
isARelation( Source, Relation, Target, Visited) :-
    \+ member( [Source,Relation,Target], Visited),
    weakest( Relation, RelationOne, RelationTwo),
    isADirectRelation( Source, RelationTwo, Intermediate),
    isARelation( Intermediate, RelationOne, Target, [[Source,RelationTwo,Intermediate]|Visited]).

write_relation( Result) :-
    write( Result), nl.

writeAllRelations :-
   setof( (Relation, Source, Target), Relation^isARelation( Source, Relation, Target, []), ListOfAllRelations),
% maplist( write_relation, ListOfAllRelations). % For SWIProlog
write( ListOfAllRelations). % for JIProlog

这有效并产生了正确的结果:

r1,e1,e2
r1,e1,e3
r1,e1,e4
r1,e2,e2
r1,e2,e3
r1,e2,e4
r1,e3,e2
r1,e3,e3
r1,e3,e4
r1,e4,e2
r1,e4,e3
r1,e4,e4
r2,e1,e3
r2,e2,e3
r2,e4,e3
r3,e1,e2
r3,e1,e4

然而,在现实世界中,有60个左右的实体和800个左右的直接关系,我还没有找到能够处理它的Prolog。我将研究Datalog。