【问题标题】:Prolog: getting max/min value via backtracking?Prolog:通过回溯获取最大值/最小值?
【发布时间】:2017-03-05 18:49:43
【问题描述】:

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

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

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

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

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

实现的风格是否简单/直接?是否存在“更好”的实现?

【问题讨论】:

  • 删除ID \= MinID 让它更快一点。

标签: prolog max min


【解决方案1】:

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

您的实现是 O(N^2) wrt 时间复杂度,因为对于每个候选谓词,所有其他谓词都被“调用”进行比较。空间复杂度为 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 的参数用于与第二个参数进行比较。

【讨论】:

  • 库(聚合)的缺点:打破约束。另一个呢?
  • 仅谈论您的编辑:如果 library(aggregate) 能够在不构建列表的情况下执行相当于 foldl 的操作,那么另一个“好”的事情(非常主观)将是。目前它只针对一组硬编码的聚合函数:计数、最小值、最大值。至少这是我从代码中得到的。
【解决方案2】:

简短的回答是,不,就始终正确、易于实施和同时高效而言,没有什么真正“更好”的了。

一个小修正:写就够了

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)。在我链接的问题和答案中,有一些如何处理这个问题的例子,但同样,我真的不认为它有任何“更好”。它可能更有效。我强烈建议您仔细考虑您的用例,并在您认为这可能是瓶颈时测量性能。

查看@CapelliC 建议的implementation of library(aggregate) 很明显,它完全适用于该用例:它可以在常量内存中找到最小值、最大值、总和等,并且只触及每个事实一次,然后回退必要时构建整个列表。

【讨论】:

  • 当然,library(aggregate) 在您需要聚合时显示了它的用处。 Sum 无法以相同的 OP 风格求解。那么,使用单一语法来学习涵盖 all(嗯,或多或少)SQL 提供的聚合函数不是 更好 吗?
  • @CapelliC 绝对,我试图将使用 bagof 或 findall 手动操作与使用库(聚合)进行比较。
  • 我不明白你的意思......从所有指标来看,必须编写一个程序,即使是我们正在讨论的那个愚蠢的程序,似乎是一个不太好的选择 WRT学习使用图书馆。这不是今天 all SW 的基本属性吗?
  • @CapelliC 现在我不确定我是否理解你的意思。如果您的意思是学习使用库比自己动手更有用,那么,是的,但是选择库需要您从自己动手中获得的那种理解。还是我错过了你的意思?
  • 如果 (from doing it yourself) 是真的,那么就不会有好的库存在 :)
猜你喜欢
  • 1970-01-01
  • 2018-03-11
  • 2021-06-02
  • 2017-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-30
  • 1970-01-01
相关资源
最近更新 更多