Prolog 在列表中查找第k个最大元素的程序

Prolog 在列表中查找第k个最大元素的程序,prolog,Prolog,我正在为kth_最大值(Xs,K)编写一个逻辑程序,该程序实现了查找 列表Xs的第K个最大元素K。该算法具有以下步骤: 将列表分成五个元素的组 有效地找到每个组的中位数,这可以用固定数量的 比较 递归查找中间值的中间值 根据中位数对原始列表进行分区 递归地在适当的较小列表中查找第k个最大元素 我该怎么做呢?我可以从列表中选择一个元素,但我不知道如何使用上述过程获得最大值 select(X; HasXs; OneLessXs) % The list OneLessXs is the resul

我正在为kth_最大值(Xs,K)编写一个逻辑程序,该程序实现了查找 列表Xs的第K个最大元素K。该算法具有以下步骤:

  • 将列表分成五个元素的组
  • 有效地找到每个组的中位数,这可以用固定数量的 比较
  • 递归查找中间值的中间值
  • 根据中位数对原始列表进行分区
  • 递归地在适当的较小列表中查找第k个最大元素
  • 我该怎么做呢?我可以从列表中选择一个元素,但我不知道如何使用上述过程获得最大值

    select(X; HasXs; OneLessXs)  
    % The list OneLessXs is the result of removing
    % one occurrence of X from the list HasXs.
    select(X,[X|Xs],Xs).
    select(X,[Y|Ys],[Y|Zs]) :-  select(X,Ys,Zs).
    

    因为还没有人尝试回答,所以我要跳进去,希望能对编程过程有所启发

    我发现维基百科上的文章对理解这种类型的“快速”(最坏情况下的线性时间)算法有很大帮助

    但是你在问题的最后问的是一个比较简单的问题。您写道“我该怎么做?我可以从列表中选择一个元素,但我不知道如何使用上述过程获得最大的元素。”(重点由我添加)

    现在,对于是否要实现“上述过程”(这是一种通过连续搜索中位数来查找第k个最大元素的通用方法),或者您是否询问如何使用该方法仅查找最大元素(一种特殊情况),似乎有点困惑。请注意,配方中没有明确使用查找最大元素的步骤来定位中间值或第k个最大元素

    但是您给出了查找列表的一个元素以及删除该元素后该列表的其余部分的代码,该元素是一个不确定的谓词,允许通过列表的所有成员进行回溯

    查找最大元素的任务是确定性的(至少在所有元素都不同的情况下是如此),这比一般选择第k个最大元素(与顺序统计相关的任务)更容易

    让我们给出一些简单的、希望明显正确的代码来查找最大的元素,然后讨论一种更优化的方法

    maxOfList(H,[H|T]) :-  upperBound(H,T), !.
    maxOfList(X,[_|T]) :-  maxOfList(X,T).
    
    upperBound(X,[ ]).
    upperBound(X,[H|T]) :-
        X >= H,
        upperBound(X,T).
    
    这个想法应该是可以理解的。我们查看列表的头部,并询问该条目是否为列表其余部分的上限。如果是这样,那一定是最大值,我们完成了(切割使其具有确定性)。如果不是,那么最大值必须出现在列表的后面,因此我们放弃head并继续递归搜索作为所有后续元素上限的条目。这里的切割是必要的,因为我们必须在第一个这样的条目时停止,以便知道它是原始列表的最大值

    我们使用了一个辅助谓词upperBound/2,这并不少见,但是这个实现的总体复杂性是列表长度中最坏情况下的二次型。所以还有改进的余地

    让我在这里停顿一下,以确保我在回答你的问题时不会完全偏离正轨。毕竟,您可能想问如何使用“上述过程”来查找kth最大元素,因此我所描述的内容可能过于专业化。然而,它可能有助于理解一般选择算法的聪明,理解简单情况下的微妙优化,找到最大元素

    添加:

    直觉上,我们可以减少最坏情况下所需的比较次数 通过浏览列表并跟踪发现的最大值“因此 远”。在过程语言中,我们可以通过重新分配 变量的值,但Prolog不允许我们直接这样做

    相反,Prolog的方法是引入一个额外的参数和 通过调用辅助谓词定义谓词maxOfList/2 有三个论点:

    maxOfList(X,[H|T]) :- maxOfListAux(X,H,T).
    
    然后可以使用maxOfListAux/3中的额外参数跟踪 “迄今为止”的最大值如下:

    maxOfListAux(X,X,[ ]).
    maxOfListAux(Z,X,[H|T]) :-
        ( X >= H  -> Y = X ; Y = H ),
        maxOfListAux(Z,Y,T).
    
    这里,maxOfListAux的第一个参数代表关于 这是列表中最大的元素,但直到我们 我已经清空了名单。所以这里的第一个条款“确定”了答案 当这种情况发生时,将第一个参数与第二个参数统一起来 (到目前为止的最大值)刚好在列表末尾达到 结束

    maxOfListAux的第二个子句使第一个参数未绑定,并且 相应地“更新”第二个参数作为列表的下一个元素 是否超过上一个最大值

    在这种情况下,没有必要使用辅助谓词, 因为我们可能通过使用 列表的标题,而不是一个额外的参数:

    maxOfList(X,[X]) :- !.
    maxOfList(X,[H1,H2|T]) :-
        ( H1 >= H2  -> Y = H1 ; Y = H2 ),
        maxOfList(X,[Y|T]).
    

    请参阅页面以获取设置帖子格式的帮助。问题来自练习。下一次,当你要求一个教科书练习的解决方案时,你能说出来源吗?你的解决方案非常有效。但我想要的是使用上面提到的过程,在其中搜索连续的中间点。你不必使用select,这只是一个想法。但我如何实施中间值程序呢。除此之外,你的解决方案简单明了。我也经常看到辅助谓词这个词,但它是什么呢。它是一个本身有一个递归目标的子句吗?@Wasswa-Samuel:辅助谓词就像一个helper函数,通常通过将我们需要的谓词定义扩展为允许最后一次调用优化(尾部递归)的形式来“帮助”它。在列表中查找最大元素的更有效的方法就是一个例子。让我来解释一件事,然后我们来看看“中间值”的概念以及它是如何实现的