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

lin*_*nda 9 list prolog

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

假设谓词是secondlargest(+List,?Val)成功的,如果Val是第二大元素List.

如果有最大的并列,那么第二大与最大的相同......

sha*_*rky 7

这是O(n)的一种方式.首先,起始谓词,(slne/2).鉴于我们是第二大元素,我们假设您有一个长度至少为2的输入列表(数字).通过比较前两个元素的相对值并记录当前最大值和当前"第二大"(如前面提到的Priyank所建议的)作为调用另一个谓词来完成工作(slne/4)的参数来解决问题:

slne([E0, E1|Es], Res) :-
    E0 > E1, !,
    slne(Es, E0, E1, Res).
slne([E0, E1|Es], Res) :-
    slne(Es, E1, E0, Res).
Run Code Online (Sandbox Code Playgroud)

其次,现在我们已经有了一个引用的起始点,通过列表的其余部分(如果有的话)递归并返回第二个最大的元素(SecMax),在(slne/4)中.

基本案例:不再剩下任何元素!我们完成了.

slne([], _, Res, Res).
Run Code Online (Sandbox Code Playgroud)

下一种情况:我们在列表的开头找到一个新的本地最大值:

slne([E|Es], Max, _SecMax, Res) :-
    E >= Max, !,
    slne(Es, E, Max, Res).
Run Code Online (Sandbox Code Playgroud)

(请注意,我们丢弃了当前的第二大(SecMax),因为我们假设它小于或等于Max).

下一个案例:我们没有找到新的本地最大值,但我们确实找到了新的"第二好":

slne([E|Es], Max, SecMax, Res) :-
    E >= SecMax, !,
    slne(Es, Max, E, Res).
Run Code Online (Sandbox Code Playgroud)

最后一种情况:抛弃其他价值观,因为它们无关紧要 - 请看看其余的价值:

slne([_|Es], Max, SecMax, Res) :-
    slne(Es, Max, SecMax, Res).
Run Code Online (Sandbox Code Playgroud)

而已.就个人而言,我喜欢Juanjo的解决方案,主要是因为它是1行代码,并且即使对于大输入,O(n log n)和O(n)之间的性能差异在实践中也可以忽略不计.(另请注意,dsort/2并未出现在所有PROLOG实现中 - 例如,在SWI-PL中,您可能会尝试msort/2使用).

  • 明确.固体.坚定......走的路! (2认同)

rep*_*eat 5

以下是使用size-3 排序网络与结合使用的:

:- 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进行示例查询:

| ?- zs_secondlargest([2,4,8,3,5,8,7], X).
X = 8 ? ;            %     8  >= 8
no
| ?- zs_secondlargest([6,4,3,6,3,4,8,6], X).
X = 6 ? ;            %             8>6
no
| ?- zs_secondlargest([2,3,5,4,1,6,7,3,9], X).
X = 7 ? ;            %             7 < 9
no
| ?- zs_secondlargest([2,3,5,4,1,6,7,3,9,8], X).
X = 8 ?              %                 9>8
yes
Run Code Online (Sandbox Code Playgroud)


Pri*_*lia 3

不知道序言,但一般来说我们不能存储两个变量,一个用于最高的变量,一个用于第二高的变量。

if(a >= x)
  b = a
  a = x
Run Code Online (Sandbox Code Playgroud)

  • 实际上,Prolog 是一种非常明智的方法,您只需将其转换为声明式风格即可。在这种情况下,这将是一个递归谓词,它扫描列表并跟踪迄今为止看到的两个最大元素。算法只是解决问题的抽象方法,通常可以在任何编程范式中实现。与任何语言一样,首选的编码风格通常是相当主观的。有时在 Prolog 中使用非声明性方法会很方便,例如故障驱动循环。 (2认同)
  • 我支持你谦虚的咖啡。这个答案只是提示了如何解决这个问题。正如您所说,可以跟踪每次传递中最大的元素。@Juanjo 同样,不存在命令式或声明式算法这样的东西。算法就是算法就是算法 (2认同)