我将加入,因为没有人尝试过回答,并希望对要编程的过程有所了解。
我发现 Selection algorithm 上的 Wikipedia 文章对于理解此类“快速”(最坏情况线性时间)算法的全局非常有帮助。
但是您在问题末尾提出的问题比较简单。您写道“我该怎么做?我可以从列表中选择一个元素,但我不知道如何使用上述过程获得最大的。” (重点由我添加)
现在,您是否要实施“上述过程”似乎有点困惑,这是通过连续搜索中位数来找到第 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).
这个想法应该是可以理解的。我们查看列表的头部并询问该条目是否是列表其余部分的上限。如果是这样,那必须是最大值,我们就完成了(切割使其具有确定性)。如果不是,则最大值必须出现在列表的后面,因此我们丢弃头部并继续递归搜索作为所有后续元素上限的条目。剪切在这里是必不可少的,因为我们必须在第一个这样的条目处停下来才能知道它是原始列表的最大值。
我们使用了一个辅助谓词 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]).