Related
I like the idea of lazy_findall as it helps me with keeping predicates separated and hence program decomposition.
What are the cons of using lazy_findall and are there alternatives?
Below is my "coroutine" version of the branch and bound problem.
It starts with the problem setup:
domain([[a1, a2, a3],
[b1, b2, b3, b4],
[c1, c2]]).
price(a1, 1900).
price(a2, 750).
price(a3, 900).
price(b1, 300).
price(b2, 500).
price(b3, 450).
price(b4, 600).
price(c1, 700).
price(c2, 850).
incompatible(a2, c1).
incompatible(b2, c2).
incompatible(b3, c2).
incompatible(a2, b4).
incompatible(a1, b3).
incompatible(a3, b3).
Derived predicates:
all_compatible(_, []).
all_compatible(X, [Y|_]) :- incompatible(X, Y), !, fail.
all_compatible(X, [_|T]) :- all_compatible(X, T).
list_price(A, Threshold, P) :- list_price(A, Threshold, 0, P).
list_price([], _, P, P).
list_price([H|T], Threshold, P0, P) :-
price(H, P1),
P2 is P0 + P1,
P2 =< Threshold,
list_price(T, Threshold, P2, P).
path([], []).
path([H|T], [I|Q]) :-
member(I, H),
path(T, Q),
all_compatible(I, Q).
The actual logic:
solution([], Paths, Paths, Value, Value).
solution([C|D], Paths0, Paths, Value0, Value) :-
( list_price(C, Value0, V)
-> ( V < Value0
-> solution(D, [C], Paths, V, Value)
; solution(D, [C|Paths0], Paths, Value0, Value)
)
; solution(D, Paths0, Paths, Value0, Value)
).
The glue
solution(Paths, Value) :-
domain(D),
lazy_findall(P, path(D, P), Paths0),
solution(Paths0, [], Paths, 5000, Value).
Here is an alternative no-lazy-findall solution by #gusbro: https://stackoverflow.com/a/68415760/1646086
I am not familiar with lazy_findall but I observe two "drawbacks" with the presented approach:
The code is not as decoupled as one might want, because there is still a mix of "declarative" and "procedural" code in the same predicate. I am putting quotes around the terms because they can mean a lot of things but here I see that path/2 is concerned with both generating paths AND ensuring that they are valid. Similarly solution/5 (or rather list_price/3-4) is concerned with both computing the cost of paths and eliminating too costly ones with respect to some operational bound.
The "bounding" test only happens on complete paths. This means that in practice all paths are generated and verified in order to find the shortest one. It does not matter for such a small problem but might be important for larger instances. Ideally, one might want to detect for instance that the partial path [a1,?,?] will never bring a solution less than 2900 without trying all values for b and c.
My suggestion is to instead use clpfd (or clpz, depending on your system) to solve both issues. With clpfd, one can first state the problem without concern for how to solve it, then call a predefined predicate (like labeling/2) to solve the problem in a (hopefully) clever way.
Here is an example of code that starts from the same "setup" predicates as in the question.
state(Xs,Total):-
domain(Ds),
init_vars(Ds,Xs,Total),
post_comp(Ds,Xs).
init_vars([],[],0).
init_vars([D|Ds],[X|Xs],Total):-
prices(D,P),
length(D,N),
X in 1..N,
element(X, P, C),
Total #= C + Total0,
init_vars(Ds,Xs,Total0).
prices([],[]).
prices([V|Vs],[P|Ps]):-
price(V,P),
prices(Vs,Ps).
post_comp([],[]).
post_comp([D|Ds],[X|Xs]):-
post_comp0(Ds,D,Xs,X),
post_comp(Ds,Xs).
post_comp0([],_,[],_).
post_comp0([D2|Ds],D1,[X2|Xs],X1):-
post_comp1(D1,1,D2,X1,X2),
post_comp0(Ds,D1,Xs,X1).
post_comp1([],_,_,_,_).
post_comp1([V1|Vs1],N,Vs2,X1,X2):-
post_comp2(Vs2,1,V1,N,X2,X1),
N1 is N+1,
post_comp1(Vs1,N1,Vs2,X1,X2).
post_comp2([],_,_,_,_,_).
post_comp2([V2|Vs2],N2,V1,N1,X2,X1):-
post_comp3(V2,N2,X2,V1,N1,X1),
N3 is N2 + 1,
post_comp2(Vs2,N3,V1,N1,X2,X1).
post_comp3(V2,N2,X2,V1,N1,X1) :-
( ( incompatible(V2,V1)
; incompatible(V1,V2)
)
-> X2 #\= N2 #\/ X1 #\= N1
; true
).
Note that the code is relatively straightforward, except for the (quadruple) loop to post the incompatibility constraints. This is due to the way I wanted to reuse the predicates in the question. In practice, one might want to change the way the data is presented.
The problem can then be solved with the following query (in SWI-prolog):
?- state(Xs, T), labeling([min(T)], Xs).
T = 1900, Xs = [2, 1, 2] ?
In SICStus prolog, one can write instead:
?- state(Xs, T), minimize(labeling([], Xs), T).
Xs = [2,1,2], T = 1900 ?
Another short predicate could then transform back the [2,1,2] list into [a2,b1,c2] if that format was expected.
First of all I have a doubt about the semantic of a program , for example :
length([],0).
length([_|L],N):-
length(L,N0),
N is N0 + 1.
The first instruction means the base case , or it has other meanings ?
I have to write a prolog program that, given a number, returns a list of numbers from 0 to the given number.
For example, when the input is 5, the output is [0,1,2,3,4,5].
I'm looking for a solution of this problem but I do not know how to start.
There is a predicate in SWI-Prologs library that does almost what you need to do. It is called numlist/3. You can use it with lower and upper boundary:
?- numlist(1, 5, L).
L = [1, 2, 3, 4, 5].
And here the implementation:
numlist(L, U, Ns) :-
must_be(integer, L),
must_be(integer, U),
L =< U,
numlist_(L, U, Ns).
numlist_(U, U, List) :-
!,
List = [U].
numlist_(L, U, [L|Ns]) :-
L2 is L+1,
numlist_(L2, U, Ns).
You can get rid of the upper half of this completely, and lose one argument (your Lower is just 1).
If you play with this a bit you should be able to figure it out.
I need to find the fastest way to travel from one city to another. I have something like
way(madrid, barcelona, 4).
way(barcelona, paris, 5).
way(madrid, londres, 3).
way(londres,paris,1).
I have come up with a predicate shortway(A,B,C,D) where C is the list of towns between A and B and D the distance.
so I have
shortway(A,B,C,D):-
way(A,B,_,_) , (A,_,C,D). D<C.
shortway(A,_,C).
I trying my best but I really cant get it to work!
You have a bunch of problems with your code! First of all, way/3 has arity 3, not 4, so calling way(A,B,_,_,) is clearly not going to do what you think. Second, I have no idea what you're trying to do with (A,_,C,D). The period after this signifies the end of the predicate! So the next line, D<C. is just a free-floating query that cannot be fulfilled. And then shortway(A,_,C) is basically a fact, with three singletons, but it would define a shortway/3 clause when the previous one is a shortway/4 clause.
There really isn't enough that's on the right track here to try and recover. It looks here like you are extremely confused about even the basics of Prolog. I would strongly encourage you to go back to the beginning and start over. You can't rush Prolog! And this code looks like you're trying to make a combustion engine by banging rocks together.
I wrote some line of code to help you, but as https://stackoverflow.com/users/812818/daniel-lyons said, it's better than you learn something easier before.
To solve your problem I advice you to read, at least, the first 3 chapters of this book: http://www.learnprolognow.org/lpnpage.php?pageid=online and do the practical session at paragraph 3.4.
Then, you could take a look at my code (you can find some explenation of it here:Out of local stack error in Prolog route planner .
Here the code
way(madrid, barcelona, 4).
way(barcelona, paris, 5).
way(madrid, londres, 3).
way(londres,paris,1).
shortway(From, To):- findall(Journey, travel(From, To, Journey, _) , Travels_list),
findall(Total_distance, travel(From, To, _, Total_distance) , Distances_list),
min_member(Y, Distances_list), find_minimum_index(Y, Distance_list, 1, Distance_index),
find_journey(Distance_index, Travels_list, 0, Shortest_path),
format('The shortest path is ~w', [Shortest_path]).
travel(From, To, Journey, Total_distance) :- dif(From, To),
AccDistance is 0,
path(From, To, [From], Journey, AccDistance, Total_distance).
path(From, To, Passed_cities, go(From, To), AccDistance, Total_distance) :- way(From, To, Way_distance),
Total_distance is AccDistance + Way_distance.
path(From, To, Passed_cities, go(From, Intermediate, GO), AccDistance, Total_distance) :- way(From, Intermediate, Way_distance),
dif(Intermediate, To),
\+ member(Intermediate, Passed_cities),
NewAccDistance is AccDistance + Way_distance,
path(Intermediate, To, [Intermediate|Passed_cities], GO, NewAccDistance, Total_distance).
min_member(Min, [H|T]) :- min_member_(T, H, Min).
min_member_([], Min, Min).
min_member_([H|T], Min0, Min) :-
( H >= Min0
-> min_member_(T, Min0, Min)
; min_member_(T, H, Min)
).
find_minimum_index(X, [], N, I) :- fail.
find_minimum_index(X, [X|T], N, I) :- I is N, !.
find_minimum_index(X, [H|T], N, I) :- H \= X, increment(N, N1), find_minimum_index(X, T, N1, I).
find_journey(I, [H|T], N, Elemento) :- N = I, Elemento = H, !.
find_journey(I, [H|T], N, Elemento) :- N \= I, increment(N, N1), find_journey(I, T, N1, Elemento).
increment(X, X1) :- X1 is X+1.
Then you call, for example
?:- shortway(madrid,paris).
and it will return
"The shortest path is go(madrid, londres, go(londres,paris))"
which distance is, 4
rather than
go(madrid, barcelona, go(barcelona, madrid)
which distance is 9.
Summing up: calling shortway/2, with the predicates findall/3 you'll find the lists of all possible pathes and their relative distances, respectively, then you'll browse the list of the distances to find the index of the minimum element and so using it to find the shortest path from the list of all pathes found previously.
I'm trying to write rules for prolog that define a median of a list by using a partitioning method.
partition([], V, [], []).
partition([X | L], V, [X | A], B) :- (V > X), !, partition(L, V, A, B).
partition([X | L], V, A, [X | B]) :- (V < X), !, partition(L, V, A, B).
partition([X | L], V, A, B) :- (V =:= X), partition(L, V, A, B).
median([A], A).
median(L, M) :- partition(L, M, A, B), length(A, X), length(B, X).
partition(L, V, A, B) partitions list L into 2 sublists A and B with A having values less than V and B having values greater than V.
That part works fine, but when I try to write my median, I'm trying to say that it is a median when after partitioning, A and B are the same length.
median works when I use concrete values, like median([1, 2, 3], 2)
but when I try median([1, 2, 3], X).
it gives an error message ERROR: >/2: Arguments are not sufficiently instantiated.
I was wondering how to fix that? Thanks!
=:= operator requires both its operands to be instantiated. When you ask for median([1, 2, 3], X), one of its operands becomes X, which is not instantiated yet. The same problem is with other arithmetic operators like >.
To correct it, you can either use constraints programming (which provides arithmetic operators that aren't so strict) or rework your program to only use arithmetic on list elements. For example, try a classical approach like: sort the list of numbers, then divide the list into three segments: list of length N, a single element, list of length N. Hint: you can do the part after sorting using just a single append/3 and two length/2 invocations.
Easy way to make your program work with median([1, 2, 3], X) query - is to instantiate M to a member of L in the last rule:
median(L, M) :-
member(M, L),
partition(L, M, A, B), length(A, X), length(B, X).
I'm having problem constructing a list of lists in my prolog program.
I have a predicate which gives me back a single case of a row. I have to group all the cases of this row and transform them into a list of lists. I can access them just fine but when I exit, all I'll get is the first element.
Here's the code:
sudoku3to2 :- s3to2(1).
s3to2(Line) :-
Line < 9,
Line1 is Line+1,
s3getLine(Line,0,[L]),
assert(sudoku2(Y,L])),
s3to2(Line1).
s3to2(9).
s3getLine(Line,X, , ) :-
X < 9,
X1 is X + 1,
sudoku3(Line,X, ),
s3getLine(Line,X1, , ).
s3getLine(Line,9,L,L).
sudoku3/3 will return the element at the X,Y coordinate. When I get to s3getLine(Line,9,L,L) I'll start going back. I want to keep all the elements I've gathered and not just the first one. And I'm really having trouble constructing the proper predicate calls.
findall/3 is the 'list constructor' more easily understood.
It's a builtin that list all solutions found, shaping the elements with a specified pattern. Here the pattern is really just the variable we are interested to.
I use between/3 to obtaing a correctly ordered matrix, without regard to sudoku3 rules order.
sudoku3(1, 1, a).
sudoku3(1, 2, b).
sudoku3(2, 1, c).
sudoku3(2, 2, d).
mat(M) :-
W = 2,
findall(Row,
(between(1, W, R),
findall(V, (between(1, W, C), sudoku3(R, C, V)), Row)
), M).
Result:
?- mat(M).
M = [[a, b], [c, d]].
You should change W=9.
HTH