我需要从列表的头部和尾部找到列表中的最大整数。我已经写了一个程序,可以找到最大的头部现在我需要一些帮助从尾部做到这一点。
这是我到目前为止所做的:
largest([X],X).
largest([X|Xs],X) :- largest(Xs,Y), X>=Y.
largest([X|Xs],N) :- largest(Xs,N), N>X.
请记住,这会从头部找到最大的整数,我需要它从尾部开始工作。谢谢你的帮助。
答案 0 :(得分:6)
等一下!在继续之前,首先测量谓词所需的时间!
?- length(J,I), I>10, append(J,[2],L),maplist(=(1),J), time(largest(L,N)). % 12,282 inferences, 0.006 CPU in 0.006 seconds (99% CPU, 1977389 Lips) J = [1, 1, 1, 1, 1, 1, 1, 1, 1|...], I = 11, L = [1, 1, 1, 1, 1, 1, 1, 1, 1|...], N = 2 ; % 4 inferences, 0.000 CPU in 0.000 seconds (84% CPU, 98697 Lips) % 24,570 inferences, 0.011 CPU in 0.011 seconds (99% CPU, 2191568 Lips) J = [1, 1, 1, 1, 1, 1, 1, 1, 1|...], I = 12, L = [1, 1, 1, 1, 1, 1, 1, 1, 1|...], N = 2 ; % 4 inferences, 0.000 CPU in 0.000 seconds (84% CPU, 98556 Lips) % 49,146 inferences, 0.021 CPU in 0.021 seconds (100% CPU, 2365986 Lips) J = [1, 1, 1, 1, 1, 1, 1, 1, 1|...], I = 13, L = [1, 1, 1, 1, 1, 1, 1, 1, 1|...], N = 2 ...
每次长度增加1时,推论的数量明显增加一倍!这就是Prolog如何通过极低效率获得其糟糕声誉的方式,并且在处理器速度方面取得了所有进展。
那么您的计划中发生了什么?没有必要详细介绍,但我们可以考虑一个程序的小片段(failure-slice)。虽然这个结果程序完全不能满足您的目的,但它为我们提供了程序中推理数量的下限:
largest([X],X) :- false. largest([X|Xs],X) :- largest(Xs,Y), false,X>=Y. largest([X|Xs],N) :- largest(Xs,N), false,N>X.
对于列表中的每个元素,我们有两个同样适用的选项。因此,对于N
元素列表,我们有2^N
个选项!
这是一个可能的重写:
largest([X],X).
largest([X|Xs],R) :-
largest(Xs,Y),
( X>=Y, R = X
; Y > X, R = N
).
使用if-then-else ...
可以做得更好largest([X],X).
largest([X|Xs],R) :-
largest(Xs,Y),
( X>=Y -> R = X
; Y > X, R = N
).
或max/2
largest([X],X).
largest([X|Xs],R) :-
largest(Xs,Y),
R is max(X,Y).
此程序仍需要与列表长度成比例的空间。通过使用尾递归版本,您可以将其减少为常量。但至少这个版本现在以线性时间运行。
对于您想要执行的实际优化,请阅读
答案 1 :(得分:1)
尾部递归头部优先解决方案如下所示:
largest( [X|Xs] , Max ) :- largest( Xs , X , Max ) .
largest( [] , R , R ) .
largest( [X|Xs] , T , R ) :- X > T , largest( Xs , X , R ) .
largest( [X|Xs] , T , R ) :- X =< T , largest( Xs , T , R ) .
largest/2
只需调用largest/3
,将其累加器用列表的头部(最初的&#39; max&#39;值)播种。当largest/3
向下递归列表时,它会用新的&#34;当前&#34;替换该累加器。遇到它们时的最大值。当列表用尽时,累加器具有整个列表的最大值。
您的初步解决方案:
largest([X],X).
largest([X|Xs],X) :- largest(Xs,Y), X>=Y.
largest([X|Xs],N) :- largest(Xs,N), N>X.
先尾跑。它会递归到列表的末尾,此时它会确定列表中的 last 项是最初的&#34; max&#34;值。当它在向上弹出堆栈时,它会将其与前一个值进行比较并完成所需的操作。
您的方法存在问题有两方面:
另一方面,尾部递归&#34;头部优先&#34;方法在O(n)时间运行:列表只迭代一次,最后你有解决方案。此外,由于尾递归优化,递归调用实质上转换为迭代,这意味着在初始堆栈帧之外不会消耗堆栈空间。这意味着可以为任何长度的列表计算解决方案(假设您愿意等待答案)。
答案 2 :(得分:1)
惯用的,尾递归的,头优先版本:
largest([X|Xs], O) :- largest(Xs, X, O).
largest([], O, O).
largest([X|Xs], M, O) :-
M1 is max(X, M),
largest(Xs, M1, O).