Prolog:通过回溯获得最大/最小值?

时间:2017-03-04 00:21:35

标签: prolog max min

我想知道通过这样的回溯获取某些事实的最大值(最老的人)是否是一个好主意:

data(MaxID, MaxName, MaxAge),
\+ (data(ID, Name, Age), ID \= MaxID, MaxAge < Age).

反之亦然最小值(最年轻的人):

data(MinID, MinName, MinAge),
\+ (data(ID, Name, Age), ID \= MinID, MinAge > Age).

这在空间或时间复杂性方面是否有效?

实现的风格是否容易/直接转发?是否存在“更好”的实现?

2 个答案:

答案 0 :(得分:2)

  

这在空间或时间复杂性方面是否有效?

你的实现它的时间复杂度为O(N ^ 2),因为对于每个候选谓词,所有其他谓词都被“调用”进行比较。空间复杂度为O(1)。

  

是否存在“更好”的实施?

是的,存在更好,更有效的实现。很长一段时间,SWI-Prolog提供了库(aggregate),这是对setof / bagof / findall经典内置的一个直接增强:

?- aggregate(max(Age,data(ID,Name,Age)), ID^Name^data(ID,Name,Age), max(_,X)).

注意由ID^Name^...表示的量化控制风格。这与setof / 3和bagof / 3非常相似。基于非回溯分配,实施更加困难。

可以将最新添加的内容视为库(solution_sequences)。 在挖掘最后一个之前,请考虑使用库(聚合)。

修改

基于@false对问题的评论和答案@boris,我会尝试提供一个'更好'(当然,主观评价)的实现:

min(P,A) :-
    copy_term(P,Q),
    arg(A,P,V), arg(A,Q,U),
    call(P), \+ ( call(Q), U@<V ).

现在,您可以将谓词作为第一个参数传递,并指定P的参数以用于与第二个参数进行比较。

答案 1 :(得分:0)

简短的回答是,不,没有真正的&#34;更好的&#34;存在,总是正确的,易于实现的,同时高效的。

一个小小的修正:写

就足够了
data(MaxID, MaxName, MaxAge),
\+ (data(ID, Name, Age), MaxAge < Age)

(正如您对问题的评论中@false所建议的那样)

您还可以看到this question and answersThis answer详细介绍了何时以及为何使用setof/3可能会出现问题。这可能对您的用例很重要。

另一种方法(在@CapelliC的非常有用的答案中没有提到)是从谓词中收集所有解决方案并使用密钥对它们进行排序。如果您正在使用SWI-Prolog或其他具有4-argument version of sort的Prolog,您可以选择比较和术语中的键,您可以这样做,例如:

bagof(data(ID, Name, Age), data(ID, Name, Age), All),
sort(3, @>=, All, [data(Max_ID, Max_Name, Max_Age)|_])

只要您的data/3只是一个事实表,就可以安全使用。

如果您在列表中有等效但不相等的元素,那么这当然会被分解为data(10, john, 34)data(101, jane, 34)。在我的链接问题和答案中,有一些例子可以解决这个问题,但同样,我真的不认为它会得到更好的&#34;更好的&#34;。它可能更有效率。我强烈建议您仔细考虑您的使用案例,如果您认为这可能是一个瓶颈,请测量性能。

查看@CapelliC建议的implementation of library(aggregate)很明显,它恰好适用于该用例:它可以在常量内存中找到最小值,最大值,总和等,并且只触及每个事实一次,并且如果有必要,可以回到建立整个清单。