【问题标题】:Prolog getting the maximum out of a collectionProlog 从集合中获得最大收益
【发布时间】:2020-04-09 19:26:28
【问题描述】:

这是一个简单的问题。

has(steve, 5).
has(mark, 6).
has(craig, 4).

在 Prolog 中你如何知道谁拥有最多?

我试过has(Who, Max) 但这没有帮助。这里有可以用的算子吗?

谢谢。

【问题讨论】:

    标签: prolog


    【解决方案1】:
    ?- has(U,S),\+((has(V,T),T>S)).
    U = mark,
    S = 6 ;
    false.
    

    前缀运算符\+ 读作not。由于我们必须反证的是由中缀运算符, 表示的连词,因此需要使用双括号。

    您应该知道,在使用它时,它是一种受限的否定形式,即所谓的negation as failure,由隐含在 Prolog 语义中的closed world assumption 实现。 p>

    或者,完全相同

    max(U,S) :- has(U,S),notanybetterthan(S).
    
    notanybetterthan(S) :- has(_,T),T>S,!,fail.
    notanybetterthan(_).
    

    max(U,S) :- has(U,S),\+anybetterthan(S).
    
    anybetterthan(S) :- has(_,T),T>S.
    

    编辑

    正如@WillNess 所指出的,我使用的语法不精确。实际上,双括号是\+ 被视为函子而不是运算符的结果。在符号后面加一个空格,我们可以改写

    ?- has(U,S),\+ (has(V,T),T>S).
    

    【讨论】:

    • 最惯用的解决方案。其中\+ 应读作“notfindany”。
    • has(U,S),\+ (has(V,T),T>S). 也有效,不能误认为是(二进制)谓词调用(注意\+ 后面的空格)。
    • @CapelliC - 草莓不喜欢\+。不过,它对not(...) 很满意。
    • @CapelliC 你还写了一次this answer,使用基于aggregate 的解决方案,以补充another answer,其中包含基于notfindall- 的代码,以及一个很好的复杂性讨论。在 cmets 中甚至讨论了 \+ 运算符之后的空格。 :)
    • @WillNess 对我来说是\+==“没有证据表明...”。
    【解决方案2】:

    试一试:

    has(steve, 5).
    has(mark, 6).
    has(craig, 4).
    
    ?- findall(has(X, Y), has(X, Y), Z), maxhas(Z, has(Who, Max)), write([Who, Max]).
    
    maxhas([has(X, Y)], has(X, Y)).
    maxhas([has(_, Y)|Hs], has(A, B)) :- maxhas(Hs, has(A, B)), B >= Y.
    maxhas([has(X, Y)|Hs], has(X, Y)) :- maxhas(Hs, has(_, B)), B < Y.
    

    我明白了:

    [mark, 6]Yes.
    

    【讨论】:

    • 您的代码复杂度最低,O(n)see also.
    【解决方案3】:

    您可以使用标准谓词findall/3keysort/2

    | ?- findall(Value-Name, has(Name, Value), Pairs),
         keysort(Pairs, SortedPairs).
    
    Pairs = [5-steve, 6-mark, 4-craig],
    SortedPairs = [4-craig, 5-steve, 6-mark]
    yes
    

    您想要SortedPairs 列表中的最后一对。只需遍历列表,直到到达最后一个元素。我会把它留给你写一个last(List, Last) 谓词。

    更新

    Carlo 的解决方案非常地道 (+1)。但这也是 O(n^2)。我的解决方案(包括缺少的 last/2 谓词)是 O(2*n + n*log(n))。另一方面,由于创建了临时列表,它对垃圾收集器的影响略大。如果我们在 OP 中只有树事实,Carlo 的解决方案要快约 3 倍。对于大约 100 个事实,两种解决方案所花费的时间大致相同(请注意,确切的数字取决于所使用的 Prolog 系统)。对于大量的事实,复杂性上的差异越来越明显。

    【讨论】:

    • 对,但可能有多个 Names 具有相同的最大 Value。这也可以在整体 O(n) 中处理(当然在 n log n 排序之上),但情节变厚(代码复杂性爆炸)。 Prolog 应该是声明式魔法! (当然不是:)(但也许可以))。不同之处在于,基于not 的代码可能会在 O(n) 步骤之后产生它的第一个答案,但基于列表的代码会预先做更多的工作,所以不能。有趣的是,使用基于列表的方法,惰性语言将总是O(n)中产生它的第一个答案...
    • ... 并且@Enigmativity 的答案总是在 O(n) 中产生结果(即使有多个,并且已对其进行了修改以处理此问题) .从最抽象的代码到最手工定制的常规方式。
    • @WillNess 对findall/3(在我的解决方案和@Enigmativity 解决方案中)的调用是 O(n)。但是随后@Enigmativity 解决方案在列表中列出了解决方案 O(2*n)。但是解决方案也不是尾递归的,这会导致惩罚 w.r.t.空间复杂度。随着使用累加器使其尾递归的变化,它应该比其他解决方案更快。
    • 所以看起来“终极”解决方案是重新实现/专门化 findall/3 本身并对其进行调整以仅找到最大元素。自己在失败驱动的循环中做所有超逻辑的断言/撤回;即最手动编码。难道真的(已经)存在一个系统,它会进入战壕并自行我们做这件事,所以我们可以坚持最高级别的声明性编码风格吗?....
    • @WillNess 不确定。可能(取决于底层应用程序)将数据简单地保存在堆数据结构中。 Logtalk 支持最大和最小堆 (logtalk3.readthedocs.io/en/latest/libraries/heaps.html) 并将在大多数 Prolog 系统中运行。这将提供 O(1) 解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-29
    • 1970-01-01
    • 2015-01-28
    • 1970-01-01
    • 2014-05-19
    • 1970-01-01
    相关资源
    最近更新 更多