我知道如何找到列表中最大的元素 - 没问题,但我应该如何找到第二最大元素?
如果secondlargest(+List,?Val)
是Val
中的第二大元素,则谓词为List
并成功。
如果存在最大的并列,则第二大与最大的相同......
答案 0 :(得分:7)
这是O(n)的一种方式。首先,起始谓词(slne/2
)。鉴于我们是第二大元素,我们假设您有一个长度至少为2的输入列表(数字)。通过比较前两个元素的相对值并记录当前最大值和当前“第二大”(如前面提到的Priyank所建议的)作为调用另一个谓词来完成工作的参数来解决问题({{1 }}):
slne/4
其次,现在我们已经有了一个引用起点,通过列表的其余部分(如果有的话)递归并返回(slne([E0, E1|Es], Res) :-
E0 > E1, !,
slne(Es, E0, E1, Res).
slne([E0, E1|Es], Res) :-
slne(Es, E1, E0, Res).
)中的第二大元素(SecMax)。
基本情况:不再剩下任何元素!我们结束了。
slne/4
下一种情况:我们在列表的开头找到一个新的本地最大值:
slne([], _, Res, Res).
(注意我们扔掉了当前的第二大(SecMax),因为我们假设它小于或等于Max)。
下一个案例:我们找不到新的本地最大值,但我们确实找到了新的“第二好”:
slne([E|Es], Max, _SecMax, Res) :-
E >= Max, !,
slne(Es, E, Max, Res).
最后一种情况:抛弃其他价值观,因为它们无关紧要 - 请看看其他价值观:
slne([E|Es], Max, SecMax, Res) :-
E >= SecMax, !,
slne(Es, Max, E, Res).
就是这样。就个人而言,我喜欢Juanjo的解决方案,主要是因为它是1行代码,并且即使对于大输入,O(n log n)和O(n)之间的性能差异在实践中也可以忽略不计。 (另请注意,slne([_|Es], Max, SecMax, Res) :-
slne(Es, Max, SecMax, Res).
并未出现在所有PROLOG实现中 - 例如,在SWI-PL中,您可能会尝试使用dsort/2
。
答案 1 :(得分:5)
以下是使用size {3 sorting networks 与clpfd结合使用的方法:
:- use_module(library(clpfd)). zs_secondlargest([Z1,Z2|Zs], X) :- T1 #= max(Z1,Z2), T2 #= min(Z1,Z2), zs_secondlargest_(Zs, X, T1, T2). zs_secondlargest_([], X, _, X). zs_secondlargest_([Z|Zs], X, A1, A2) :- B2 #= max(A2,Z), C1 #= max(min(A2,Z),A1), D1 #= max(C1,B2), D2 #= min(C1,B2), zs_secondlargest_(Zs, X, D1, D2).
使用SICStus Prolog 4.3.2的示例查询:
timeFormat: ''
答案 2 :(得分:4)
以下是我对使用if_/3的@ sharky版本的clpfd变体的建议。首先,这是使用clpfd的大于等于关系的reifying版本:
geq_to_t(X,Y,T) :-
(X#>=Y) #<==> B,
bool10_t(B,T).
bool10_t(1,true).
bool10_t(0,false).
使用geq_to_t / 3和if_/3可以像这样定义谓词:
% this corresponds to @sharky's slne/2:
sl_of(SL,[X1,X2|Xs]) :-
if_(geq_to_t(X1,X2),
max_sl_of_(X1,X2,Xs,SL),
max_sl_of_(X2,X1,Xs,SL)).
% this corresponds to @sharky's slne/4:
max_sl_of_(_M,SL,[],SL). % base case
max_sl_of_(M,SL,[X|Xs],R) :-
if_(geq_to_t(X,M), % if X#>=M
max_sl_of_(X,M,Xs,R), % X is new maximum
if_(geq_to_t(X,SL), % else if X#>=SL
max_sl_of_(M,X,Xs,R), % X is new 2nd largest
max_sl_of_(M,SL,Xs,R))). % else X is not of interest
请注意,我翻转了参数的顺序以使用更具说明性的命名(sl_of / 2),因此列表现在是第二个参数。正如赏金描述所要求的那样,如果第二个论点是基础的话,就没有任何无用的选择点:
?- sl_of(SL,[1,2,3,4,5,6]).
SL = 5
?-
来自@repeat回答的样本:
?- sl_of(SL,[2,4,8,3,5,8,7]).
SL = 8
?- sl_of(SL,[6,4,3,6,3,4,8,6]).
SL = 6
?- sl_of(SL,[2,3,5,4,1,6,7,3,9]).
SL = 7
?- sl_of(SL,[2,3,5,4,1,6,7,3,9,8]).
SL = 8
答案 3 :(得分:3)
不知道prolog,但一般不能存储两个变量,一个是最高的,一个是第二个。
if(a >= x)
b = a
a = x
答案 4 :(得分:2)
我会告诉你两种不同的算法。第一个很容易,第二个有点棘手。
编写合并排序功能(降序),然后选择第二个元素。这很容易,但需要O(nlogn)时间。
通常对于任何k,您可以通过以下算法解决此问题。这是在线性时间内运行的。
- 将列表分成五个元素组 - 找出每组的中位数,这可以通过固定数量的比较来完成 - 递归地找到中位数或中位数 - 将原始列表与中位数的中位数分开 - 递归地在适当的较小列表中找到第k个最大元素。
您可以在“Cormen算法简介”第9章中找到有关此算法的更详细讨论
我建议您也尝试自己实现它,而不会看到任何现有的实现。我在prolog中实现这个算法很有趣:)
答案 5 :(得分:2)
这是你在Prolog中的表现方式:
secondLargest(L, Y) :- dsort(L, [X,Y|T])
dsort的工作方式如下:
?- dsort([2,3,4,2,1], L).
L = [4,3,2,2,1]
你可以自己实现dsort,hacky部分就是我编写列表[X,Y | T]的方式。这就是:列表由2个元素(X和Y)和一个尾(T)组成。
答案 6 :(得分:1)
这是一种用于任何语言的习语:
循环遍历列表,计算每个元素并插入另一个列表数据以告诉您元素及其大小。按降序对列表进行排序。将列表指针移动到第二个元素。你现在拥有第二大元素。
答案 7 :(得分:1)
第一项任务:实现排序谓词,如果现在超出你的能力,请查看Clocksin和Mellish。
第二个任务:编写一个谓词,选择列表尾部的头部。将此谓词应用于排序列表。
OR,因为您已经知道如何选择列表中的最大元素,所以编写一个谓词来删除列表中最大的元素,并将现有谓词应用于原始列表的其余部分。
答案 8 :(得分:1)
另一种(不是最明显的)方法如下: 1.在列表L中找到最大元素 2.从列表L中删除max元素并获取新列表L1 3.在L1中找到最大值并将其返回
答案 9 :(得分:1)
有时dsort在我的SWI-Prolog的某些版本中不起作用。 所以,这是最简单的代码:
secondLargest(List,X):-
msort(List,SortedList),reverse(SortedList,[_|[X|_]]).