Prolog 在列表中查找第二个最小值

Prolog 在列表中查找第二个最小值,prolog,Prolog,我已经定义了在列表中查找最小值的谓词,例如 greater(X,Y):- X > Y. isLower(X,Y):- X < Y. findmin( [X] , X ). findmin( [H|T] , P ):- findmin(T,P1) , isLower(H,P1), P is H. findmin( [H|T] , P) :- findmin(T,P1) , greater(H , P1), P is P1. 更大(X,Y):-X>Y。 岛下(X,Y):-X=H

我已经定义了在列表中查找最小值的谓词,例如

greater(X,Y):- X > Y.
isLower(X,Y):- X < Y.


findmin( [X]   , X ).
findmin( [H|T] , P ):- findmin(T,P1) , isLower(H,P1), P is H.
findmin( [H|T] , P) :- findmin(T,P1) , greater(H , P1), P is P1.
更大(X,Y):-X>Y。
岛下(X,Y):-X
然而,我很难修改这段代码来找到第二个最小值,包括嵌套列表


如何确保返回第二个最小值?

您可以简单地创建一个包含两个最小值的列表。然后,您需要检查每个元素是否小于
P
中的较大元素,如果是,则更换它。最后,两者中较大的一个是第二大元素


顺便说一句,你真的不需要定义你自己的
greater
/
isLower
-为什么不使用内置的操作符,
/
我的意思主要是开玩笑,但这里是:

find_second_min(L, Min2) :- sort(L, [_, Min2|_]).
所以,分类肯定会把你所有的物品整理好。你只需看上面的一个就可以得到最小值。如果您想要两个最小的元素,可以查看前两个元素:

find_mins(L, Min1, Min2) :- sort(L, [Min1, Min2|_]).

正如您所知,排序是O(N logn),而您可以在O(N)中找到最小值。因此,要找到一个值,这可能是太多的工作了。但是它很可爱。

首先,您对
findmin/2
的实现不是很有效:

findmin([H|T],P):- findmin(T,P1) , isLower(H,P1), P is H.
因为它不使用尾部递归。最好将其转换为带有尾部递归和累加器的子句

正如@DanielLyons所建议的那样,您可以对列表进行排序,然后检索第二个元素,但这需要O(n logn)时间复杂性来完成排序步骤。然而,在大多数Prolog系统中,排序是在低级C中完成的;但最终,如果列表非常大,线性近似法的性能将优于它

您还可以使用@FelixDombek的方法,将这两个值存储到一个列表中。可以通过以下方式略微提高该方法的效率:

  • 不使用列表,只使用两个参数,因为这样可以节省模式匹配和元组构造的时间;及
  • 使用一种方法保存比较结果
这导致将程序转换为:

find2nd([H1,H2|T],Min2) :-
    H1 =< H2 ->
    find2nd(T,H1,H2,Min2)
   ;find2nd(T,H2,H1,Min2).

find2nd([],_,Min2,Min2).
find2nd([H|T],H1,H2,Min2) :-
    H >= H2 ->
    find2nd(T,H1,H2,Min2)
   ;(  H >= H1 ->
       find2nd(T,H1,H,Min2)
      ;find2nd(T,H,H1,Min2)
    ).
find2nd([H1,H2 | T],Min2):-
H1=
find2nd(T,H1,H2,Min2)
;find2nd(T,H2,H1,Min2)。
find2nd([],[u2,Min2,Min2)。
find2nd([H | T],H1,H2,Min2):-
H>=H2->
find2nd(T,H1,H2,Min2)
;(H>=H1->
find2nd(T,H1,H,Min2)
;查找第二个(T、H、H1、Min2)
).

查找第二个最小值(L,Min2):-sort(L,[[u,Min2 | |])。
)“包括嵌套列表”是什么意思?你能有类似于
findmin([1,2[3,0],4],0)
?s(X)当然这不是一个玩笑,这是唯一实用有效的方法。小心,原始问题使用算术比较,而
sort/2
将使用标准的术语顺序。有些事情会有不同的分类sort
是用C实现的mergesort,因此速度非常快。然而,这是一个O(N logn)算法,在某些情况下会比线性算法慢。这一点大约是N=10000,Willem的解决方案变得更快。这种方法还有另一个缺点,在大于10000的列表上,Prolog会出现堆栈溢出<代码>排序
毕竟需要返回一个全新的列表。对于10m以下的列表,它仍然是最短、最快的解决方案。+1用于“代码审查”。我通常避免使用命令式结构来获得更清晰、更具声明性的代码。但当然,在速度方面,它们确实是有用的优化。2备注:(1)如果上一个
条件
H>=H
,则上一个
中有一个错误,(2)对于非常大的列表,似乎还有其他因素在起作用。即使要处理数以百万计的项目,
排序
也比线性算法快得多。@FelixDombek:谢谢你的错误报告,我修复了它。关于效率,通过比较对列表进行排序不能比O(n logn)更好,这是一个可以很容易证明的理论结果。因此,最终O(n)将超越它。然而,请注意,与Prolog中的解释实现相比,使用C实现将非常有效,因为调用和模式匹配的成本可能相当昂贵。我知道这一点,但是,线性算法的运行时间并没有真正线性扩展。你自己试试吧!理论效率和实际时间之间的差异只会在N非常大的情况下变得更大。这就是为什么必须存在其他隐藏的因素。事实上,我想问一个关于这个的问题,因为这真的有点奇怪。@FelixDombek:目前我在工作,你能问一个显示基准图的问题吗?我会在工作时间后再看一看。
find2nd([H1,H2|T],Min2) :-
    H1 =< H2 ->
    find2nd(T,H1,H2,Min2)
   ;find2nd(T,H2,H1,Min2).

find2nd([],_,Min2,Min2).
find2nd([H|T],H1,H2,Min2) :-
    H >= H2 ->
    find2nd(T,H1,H2,Min2)
   ;(  H >= H1 ->
       find2nd(T,H1,H,Min2)
      ;find2nd(T,H,H1,Min2)
    ).