in short: How to find min value in a list? (thanks for the advise kaarel)
long story:
I have created a weighted graph in amzi prolog and given 2 nodes, I am
It is common to use a so-called "lagged argument" to benefit from first-argument indexing:
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).
This pattern is called a fold (from the left), and foldl/4
, which is available in recent SWI versions, lets you write this as:
list_min([L|Ls], Min) :- foldl(num_num_min, Ls, L, Min).
num_num_min(X, Y, Min) :- Min is min(X, Y).
Notice though that this cannot be used in all directions, for example:
?- list_min([A,B], 5).
is/2: Arguments are not sufficiently instantiated
If you are reasoning about integers, as seems to be the case in your example, I therefore recommend you use CLP(FD) constraints to naturally generalize the predicate. Instead of (is)/2
, simply use (#=)/2
and benefit from a more declarative solution:
:- 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).
This can be used as a true relation which works in all directions, for example:
?- list_min([A,B], 5).
yielding:
A in 5..sup,
5#=min(B, A),
B in 5..sup.
SWI-Prolog offers library(aggregate). Generalized and performance wise.
:- [library(aggregate)].
min(L, M) :- aggregate(min(E), member(E, L), M).
edit
A recent addition was library(solution_sequences). Now we can write
min(L,M) :- order_by([asc(M)], member(M,L)), !.
max(L,M) :- order_by([desc(M)], member(M,L)), !.
Now, ready for a surprise :) ?
?- 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 wins hands down, and to my surprise, slow_max/2 turns out to be not too bad...
thanks for the replies. been useful. I also experimented furthur and developed this answer:
% 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.
Not sure how to gauge how good of an answer this is algorithmically yet, but it works! would appreciate any feedback nonetheless. thanks!
This is ok for me :
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
SWI-Prolog has min_list/2
:
min_list(+List, -Min)
True if Min is the smallest number in List.
Its definition is in 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).
Solution without "is".
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).