17

简而言之:如何在列表中找到最小值?(感谢kaarel的建议)

很长的故事:

我在 amzi prolog 中创建了一个加权图并给定 2 个节点,我能够检索路径列表。但是,我需要在此路径中找到最小值,但无法遍历列表来执行此操作。我可以就如何确定列表中的最小值征求您的意见吗?

我的代码目前看起来像这样:

弧(1,2)。
弧(2,3)。
弧(3,4)。
弧(3,5)。
弧(3,6)。
弧(2,5)。
弧(5,6)。
弧(2,6)。

路径(X,Z,A):-
 (arc(X,Y),path(Y,Z,A1),A 为 A1+1;arc(X,Z),A 为 1)。

因此,'键入 findall(Z,path(2,6,Z),L)。' in listener 允许我获得一个列表 [3,2,2,1]。我需要从这里检索最小值并将其乘以一个数量。有人可以建议如何检索最小值吗?谢谢!

4

13 回答 13

25

通常使用所谓的“滞后参数”从第一个参数索引中受益:

list_min([L|Ls], Min) :-
    list_min(Ls, L, Min).

list_min([], Min, Min).
list_min([L|Ls], Min0, Min) :-
    Min1 is min(L, Min0),
    list_min(Ls, Min1, Min).

这种模式称为折叠(从左至右),并且foldl/4在最近的 SWI 版本中可用,您可以将其写为:

list_min([L|Ls], Min) :- foldl(num_num_min, Ls, L, Min).

num_num_min(X, Y, Min) :- Min is min(X, Y).


请注意,这不能用于所有方向,例如:

?- list_min([A,B], 5).
is/2: Arguments are not sufficiently instantiated

如果您正在推理integers,就像您的示例中的情况一样,因此我建议您使用 CLP(FD) 约束来自然地概括谓词。而不是(is)/2,只需使用(#=)/2更具声明性的解决方案并从中受益:

:- use_module(library(clpfd)).

list_min([L|Ls], Min) :- foldl(num_num_min, Ls, L, Min).

num_num_min(X, Y, Min) :- Min #= min(X, Y).

这可以用作适用于所有方向的真实关系,例如:

?- list_min([A,B], 5).

产生:

A in 5..sup,
5#=min(B, A),
B in 5..sup.
于 2010-10-19T07:30:43.927 回答
16

这对我来说是正确的(从这里)。

min_in_list([Min],Min).                 % We've found the minimum

min_in_list([H,K|T],M) :-
    H =< K,                             % H is less than or equal to K
    min_in_list([H|T],M).               % so use H

min_in_list([H,K|T],M) :-
    H > K,                              % H is greater than K
    min_in_list([K|T],M).               % so use K
于 2010-10-19T03:25:30.983 回答
5
%Usage: minl(List, Minimum).
minl([Only], Only).
minl([Head|Tail], Minimum) :-
    minl(Tail, TailMin),
    Minimum is min(Head, TailMin). 

第二条规则进行递归,用英文“获取尾部的最小值,并将最小值设置为该值和头部中的较小值”。第一条规则是基本情况,“一个列表的最小值,是列表中的唯一值”。

测试:

| ?- minl([2,4,1],1).

true ? 

yes
| ?- minl([2,4,1],X).

X = 1 ? 

yes

您可以使用它来检查第一种情况下的值,或者您可以让 prolog 计算第二种情况下的值。

于 2011-05-02T16:06:56.283 回答
3

SWI-Prolog 提供库(聚合)。通用和性能明智。

:- [library(aggregate)].
min(L, M) :- aggregate(min(E), member(E, L), M).

编辑

最近添加的是库(solution_sequences)。现在我们可以写

min(L,M) :- order_by([asc(M)], member(M,L)), !.
max(L,M) :- order_by([desc(M)], member(M,L)), !.

现在,准备好惊喜 :) ?

?- test_performance([clpfd_max,slow_max,member_max,rel_max,agg_max]).
clpfd_max:99999996
% 1,500,000 inferences, 0.607 CPU in 0.607 seconds (100% CPU, 2470519 Lips)
slow_max:99999996
% 9,500,376 inferences, 2.564 CPU in 2.564 seconds (100% CPU, 3705655 Lips)
member_max:99999996
% 1,500,009 inferences, 1.004 CPU in 1.004 seconds (100% CPU, 1494329 Lips)
rel_max:99999996
% 1,000,054 inferences, 2.649 CPU in 2.648 seconds (100% CPU, 377588 Lips)
agg_max:99999996
% 2,500,028 inferences, 1.461 CPU in 1.462 seconds (100% CPU, 1710732 Lips)
true 
with these definitions:

```erlang
:- use_module(library(clpfd)).

clpfd_max([L|Ls], Max) :- foldl([X,Y,M]>>(M #= max(X, Y)), Ls, L, Max).

slow_max(L, Max) :-
   select(Max, L, Rest), \+ (member(E, Rest), E @> Max).

member_max([H|T],M) :-
    member_max(T,N), ( \+ H@<N -> M=H ; M=N ).
member_max([M],M).

rel_max(L,M) :-
    order_by([desc(M)], member(M,L)), !.

agg_max(L,M) :-
    aggregate(max(E), member(E,L), M).

test_performance(Ps) :-
    test_performance(Ps,500 000,_).
test_performance(Ps,N_Ints,Result) :-
    list_of_random(N_Ints,1,100 000 000,Seq),
    maplist({Seq}/[P,N]>>time((call(P,Seq,N),write(P:N))),Ps,Ns),
    assertion(sort(Ns,[Result])).

list_of_random(N_Ints,L,U,RandomInts) :-
    length(RandomInts,N_Ints),
    maplist({L,U}/[Int]>>random_between(L,U,Int),RandomInts).

clpfd_max 赢得了胜利,令我惊讶的是,slow_max/2 结果还不错......

于 2011-12-07T11:30:03.677 回答
2

SWI-Prolog 有min_list/2

min_list(+List, -Min)
    True if Min is the smallest number in List.

它的定义在library/lists.pl

min_list([H|T], Min) :-
    min_list(T, H, Min).

min_list([], Min, Min).
min_list([H|T], Min0, Min) :-
    Min1 is min(H, Min0),
    min_list(T, Min1, Min).
于 2010-10-19T06:26:14.643 回答
2

这对我来说没问题:

minimumList([X], X).        %(The minimum is the only element in the list)

minimumList([X|Q], M) :-    % We 'cut' our list to have one element, and the rest in Q
 minimumList(Q, M1),         % We call our predicate again with the smallest list Q, the minimum will be in M1
 M is min(M1, X).            % We check if our first element X is smaller than M1 as we unstack our calls

于 2019-03-13T13:57:47.297 回答
2

这个程序可能很慢,但我喜欢尽可能写出明显正确的代码。

最小(列表,最小值):- 排序(列表,[Min |_])。

于 2019-02-26T03:35:04.443 回答
1

与 andersoj 类似,但使用切割而不是双重比较:

min([X], X).

min([X, Y | R], Min) :-
    X < Y, !,
    min([X | R], Min).

min([X, Y | R], Min) :-
   min([Y | R], Min).
于 2012-05-06T19:07:30.027 回答
1

没有“是”的解决方案。

min([],X,X).
min([H|T],M,X) :- H =< M, min(T,H,X).
min([H|T],M,X) :- M < H, min(T,M,X).
min([H|T],X) :- min(T,H,X).
于 2012-08-03T08:57:36.583 回答
0
min([Second_Last, Last], Result):-
    Second_Last < Last
 -> Result = Second_Last
 ;  Result = Last, !.

min([First, Second|Rest], Result):-
    First < Second
 -> min([First|Rest], Result)
 ;  min([Second|Rest], Result).

应该工作。

于 2011-12-07T09:28:24.327 回答
0

感谢您的回复。很有用。我还进行了进一步的实验并开发了这个答案:

% if list has only 1 element, it is the smallest. also, this is base case.
min_list([X],X).

min_list([H|List],X) :-
min_list(List,X1), (H =< X1,X is H; H > X1, X is X1).

% recursively call min_list with list and value,
% if H is less than X1, X1 is H, else it is the same. 

不知道如何衡量这个答案在算法上有多好,但它确实有效!尽管如此,仍将不胜感激任何反馈。谢谢!

于 2010-10-19T14:58:30.200 回答
0

这很有效,而且看起来相当有效。

min_in_list([M],M).    
min_in_list([H|T],X) :-
    min_in_list(T,M),
    (H < M, X = H; X = M).   

min_list(X,Y) :- min_in_list(X,Y), !.
于 2016-03-08T02:01:21.547 回答
-1

% 在列表中找到最小值

min([Y],Y):-!.

min([H|L],H):-min(L,Z),H=<Z.

min([H|L],Z):-min(L,Z),H>=Z.

%所以whattaya认为!

于 2013-05-18T07:41:53.930 回答