问题
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 able to retrieve a list of paths. However, I need to find the minimum value in this path but am unable to traverse the list to do this. May I please seek your advise on how to determine the minimum value in the list?
my code currently looks like this:
arc(1,2). arc(2,3). arc(3,4). arc(3,5). arc(3,6). arc(2,5). arc(5,6). arc(2,6). path(X,Z,A) :- (arc(X,Y),path(Y,Z,A1),A is A1+1;arc(X,Z), A is 1).
thus, ' keying findall(Z,path(2,6,Z),L).' in listener allows me to attain a list [3,2,2,1]. I need to retrieve the minimum value from here and multiply it with an amount. Can someone please advise on how to retrieve the minimum value? thanks!
回答1:
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.
回答2:
This looks right to me (from here).
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
回答3:
SWI-Prolog offers library(aggregate). Generalized and performance wise.
:- [library(aggregate)].
min(L, M) :- aggregate(min(E), member(E, L), M).
回答4:
%Usage: minl(List, Minimum).
minl([Only], Only).
minl([Head|Tail], Minimum) :-
minl(Tail, TailMin),
Minimum is min(Head, TailMin).
The second rule does the recursion, in english "get the smallest value in the tail, and set Minimum to the smaller of that and the head". The first rule is the base case, "the minimum value of a list of one, is the only value in the list".
Test:
| ?- minl([2,4,1],1).
true ?
yes
| ?- minl([2,4,1],X).
X = 1 ?
yes
You can use it to check a value in the first case, or you can have prolog compute the value in the second case.
回答5:
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
回答6:
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).
回答7:
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).
回答8:
Similar to andersoj, but using a cut instead of double comparison:
min([X], X).
min([X, Y | R], Min) :-
X < Y, !,
min([X | R], Min).
min([X, Y | R], Min) :-
min([Y | R], Min).
回答9:
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!
回答10:
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).
Should be working.
回答11:
This works and seems reasonably efficient.
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), !.
回答12:
This program may be slow, but I like to write obviously correct code when I can.
smallest(List,Min) :- sort(List,[Min|_]).
回答13:
% find minimum in a list
min([Y],Y):-!.
min([H|L],H):-min(L,Z),H=<Z.
min([H|L],Z):-min(L,Z),H>=Z.
% so whattaya think!
来源:https://stackoverflow.com/questions/3965054/prolog-find-minimum-in-a-list