Prolog:查找列表中的第二大元素

时间:2009-12-02 07:08:56

标签: list prolog

我知道如何找到列表中最大的元素 - 没问题,但我应该如何找到第二最大元素?

如果secondlargest(+List,?Val)Val中的第二大元素,则谓词为List并成功。

如果存在最大的并列,则第二大与最大的相同......

10 个答案:

答案 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 结合使用的方法:

:- 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)

我会告诉你两种不同的算法。第一个很容易,第二个有点棘手。

  1. 编写合并排序功能(降序),然后选择第二个元素。这很容易,但需要O(nlogn)时间。

  2. 通常对于任何k,您可以通过以下算法解决此问题。这是在线性时间内运行的。

  3. - 将列表分成五个元素组 - 找出每组的中位数,这可以通过固定数量的比较来完成 - 递归地找到中位数或中位数 - 将原始列表与中位数的中位数分开 - 递归地在适当的较小列表中找到第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|_]]).