I have the following prolog program:
square([H|T], X) :-
squareCompare(T, H, X).
squareCompare([], X, X * X ).
squareCompare([H|T], V, Result) :-
(V * V) < (H * H),
squareCompare(T, V, Result);
(V * V) > (H * H),
squareCompare(T, H, Result).
When I enter:
square([7, 5, 2], Result).
I get Result = 2 * 2, what I want is Result = 4.
This program searches for the smallest square of the element in the list.
Besides the lack of arithmetic evaluation (is/2) as pointed out in the comments there's also an issue with using </2 and >/2: your predicate doesn't work for list with consecutive repetitions, e.g.:
?- square([7,7],X).
false.
where the expected result would be 49. You can remedy that by replacing </2 by =</2 or >/2 by >=/2 in your recursive rule of squareCompare/3:
squareCompare([], X, Y) :-
Y is X*X.
squareCompare([H|T], V, Result) :-
(V * V) < (H * H),
squareCompare(T, V, Result);
(V * V) >= (H * H),
squareCompare(T, H, Result).
Now the predicate yields the desired result:
?- square([7,7],X).
X = 49.
Following another suggestion in the comments, you could opt to use CLP(FD) to make the predicate work both ways. In that case the predicate resembles a true relation so it'd be appropriate to give it a more descriptive name that reflects this fact, say list_minsquare/2. And since you are interested in the smallest square, why not pass around the squares as arguments rather than the numbers? Worst case: the root of the smallest square is the last list element, then there's no difference. Best case: the root of the smallest square is the first list element, then you only calculate it once instead of length-of-list times. Putting all this together:
:- use_module(library(clpfd)).
list_minsquare([H|T],X) :-
S #= H*H,
list_square_minsquare(T,S,X).
list_square_minsquare([],S,S).
list_square_minsquare([H|T],S,Result) :-
S #< (H*H),
list_square_minsquare(T,S,Result).
list_square_minsquare([H|T],S,Result) :-
H2 #= (H*H),
S #>= H2,
list_square_minsquare(T,H2,Result).
Now let's see some action. Your example query yields the desired result:
?- list_minsquare([7,4,2],X).
X = 4.
Consecutive repetitions also don't cause troubles:
?- list_minsquare([7,7],X).
X = 49.
Partially instantiated lists lead to all possible solutions being produced:
?- list_minsquare([7,Y,2],X).
X = 4, % <- 1st answer: X=4 if
Y^2#=_G670,
_G670 in 50..sup ; % Y^2 is between 50 and sup
Y in -1..1, % <- 2nd answer: if Y in -1..1
Y^2#=X, % then X=Y^2
X in 0..1 ;
X = 4, % <- 3rd answer: X=4
Y in -7.. -1\/1..7, % if Y in -7..-1 or 1..7
Y^2#=_G1754,
_G1754 in 4..49. % and Y^2 in 4..49
In the above example there are three possibilities for Y none of which has a unique solution, hence you get residual goals in the answers. If you wish to get concrete solutions you can constrain the range of Y and ask for concrete numbers with label/1:
?- Y in 0..3, list_minsquare([7,Y,2],X), label([Y]).
Y = X, X = 0 ;
Y = X, X = 1 ;
Y = 2,
X = 4 ;
Y = 3,
X = 4.
The most general query works as well. However, it is listing the solutions in an unfair manner:
?- list_minsquare(L,X).
L = [_G97], % <- 1st solution
_G97^2#=X,
X in 0..sup ;
L = [_G266, _G269], % <- 2nd solution
_G266^2#=X,
X in 0..sup,
X+1#=_G309,
_G309 in 1..sup,
_G332#>=_G309,
_G332 in 1..sup,
_G269^2#=_G332 ;
L = [_G494, _G497, _G500], % <- 3rd solution
_G494^2#=X,
X in 0..sup,
X+1#=_G540,
X+1#=_G552,
_G540 in 1..sup,
_G575#>=_G540,
_G575 in 1..sup,
_G500^2#=_G575,
_G552 in 1..sup,
_G620#>=_G552,
_G620 in 1..sup,
_G497^2#=_G620 ;
.
.
.
You only get one solution for every list length before moving on to the next length. You can get a fair ordering by prefixing a goal length/2 in the query. Then you'll get all possibilities for every list length before moving on:
?- length(L,_), list_minsquare(L,X).
L = [_G339], % <- 1st solution: list with one element
_G339^2#=X,
X in 0..sup ;
L = [_G1036, _G1039], % <- 2nd solution: list with two elements
_G1036^2#=X, % X is square of 1st element
X in 0..sup,
X+1#=_G1079,
_G1079 in 1..sup,
_G1102#>=_G1079,
_G1102 in 1..sup,
_G1039^2#=_G1102 ;
L = [_G935, _G938], % <- 3rd solution: list with two elements
_G935^2#=_G954,
_G954 in 0..sup,
_G954#>=X,
X in 0..sup,
_G938^2#=X ; % X is square of 2nd element
.
.
.
Of course you can also constrain and label the numbers in the list for the above query and you'll get concrete numbers in the still infinitely many solutions (since there are infinitely many list lengths).
?- length(L,_), L ins 1..2, list_minsquare(L,X), label(L).
L = [1],
X = 1 ;
L = [2],
X = 4 ;
L = [1, 2],
X = 1 ;
L = [1, 1],
X = 1 ;
L = [2, 1],
X = 1 ;
L = [2, 2],
X = 4 ;
L = [1, 2, 2],
X = 1 ;
L = [1, 2, 1],
X = 1 ;
L = [1, 1, 2],
X = 1 ;
L = [2, 1, 2],
X = 1 ;
.
.
.
Related
I have a prolog rule position_that_is_equals_to_two that sets X to the position at which the number 2 was found in the provided list of three elements [X, Y, Z]:
position_that_is_equals_to_two([X, Y, Z], X) :-
include(==(2), [X, Y, Z], AllElementsWhichHaveAValueOfTwo),
nth0(0, AllElementsWhichHaveAValueOfTwo, X).
When querying it, I immediately get false:
?- position_that_is_equals_to_two([X, _, _], X)
false
However, when I replace include/3 with individual comparisons, prolog gives three possible values for X, which is the output I would expect:
position_that_is_equals_to_two([X, Y, Z], X) :-
(
( X == 2 ; X #= 1)
; ( Y == 2 ; X #= 2)
; ( Z == 2 ; X #= 3)
).
Querying it:
?- position_that_is_equals_to_two([X, _, _], X)
X = 1
X = 2
X = 3
Why is the first variant returning false? How can it me modified to (1) still use include and (2) list possible values for X, like the second variant does?
How can it be modified to still use include?
It can't. Include shrinks the original list and throws away information you need to answer the question. With AllElementsWhichHaveAValueOfTwo = [2] what is the index of that two? Was it 0, 1, 2 or 50,000? You can't know.
Worse, include/3 has the signature include(:Goal, +List1, ?List2) and the + means the List1 must be provided, you can't give it unground variables like [X,Y,Z] and have it fill them in. So it can't be used for that reason also.
Take this query:
?- position_that_is_equals_to_two([X, _, _], X)
What you expect out of it is that X in the list has value two and X as the index has value zero. You want 2 = 0. That can't work.
Your other code is giving the right answer for the wrong reasons; the code (X == 2 ; X #= 1) says "variable X must be two OR variable X must be one" which is allowed but for your indexing you need them both at the same time, not either/or. What you want it to say is "first list item must be two AND the index must be one".
Change the code to (X = 2, X = 1) which is logically how it should be and you're back to asking for 2 = 1 which can't work.
In your example code, X is being used for 2 different purposes and values - that's a conflict.
== is not clpfd.
Looks like this would be sufficient (without using clpfd):
pos_2(Pos, L) :-
length(L, 3),
nth1(Pos, L, 2).
Result in swi-prolog:
?- pos_2(Pos, L).
Pos = 1,
L = [2, _, _] ;
Pos = 2,
L = [_, 2, _] ;
Pos = 3,
L = [_, _, 2].
I am new to prolog and have am trying to write a program that will do the following tell me if a number is between 2 values I can do the following:
between(L, X, R) :-
X > L, X < R.
and doing between(1, 3, 5) works, but I would like it to be able to do between(1, X, 5) and have prolog return all the values in between so in this case X = 2, X = 3, X = 4, I get why my solution doesn't because it needs to be have been initialised, but I cannot think of a solution to this problem, can this type of thing just not be done in prolog?, and help would be great thanks
In case you don't want to predefine all numbers: let prolog create a list with possible entries and state your X has to be one of them. To understand the code you have to have knowledge about lists in prolog, especially Head and Tail notation of lists.
betweenList(L,R,[]):-
L>=R.
betweenList(L,R,[L|Rest]):-
L<R,
LL is L+1,
betweenList(LL,R,Rest).
between(L, X, R) :-
betweenList(L, R, [L| List]),
member(X, List).
?- between(1,X,5).
X = 2 ;
X = 3 ;
X = 4 ;
false.
betweenList(L,R,List) creates a List of all numbers between L and R, including L (as head element), excluding R. So if you want to generate a List without L, it is the easiest to just call betweenList(L, R, [L| List]) so List will not include L. Now X just has to be a member of List. The member/2 predicate can be easily written as well if you don't want to use the inbuild predicate.
One way to approach this:
digit(0).
digit(1).
digit(2).
digit(3).
digit(4).
digit(5).
digit(6).
digit(7).
digit(8).
digit(9).
between(L, X, U) :- digit(L), digit(X), digit(U), L < X, X < U.
Tests:
?- between(2, X, 5).
X = 3 ;
X = 4 ;
false.
?- between(2, 7, U).
U = 8 ;
U = 9.
Alternatively, you may want to look into Constraint logic programming.
Incidentally, Prolog already has a between/3:
?- between(1, 5, X).
X = 1 ;
X = 2 ;
X = 3 ;
X = 4 ;
X = 5.
although it's "illogical": you can't run it backwards, as the above definition.
I'm studying prolog and I want to determine all decomposition of n (n given, positive), as sum of consecutive natural numbers but I don't know how to approach this.
Any ideas ?
The key here is between/3, which relates numbers and ranges. Prolog is not going to conjure up numbers from thin air, you have to give it some clues. In this case, you can assume a range of numbers between 1 and the n which you are given:
decomp2(N, X, Y) :-
between(1, N, X),
between(1, N, Y),
N =:= X + Y.
This will give you the sum of two numbers that yields N:
?- decomp2(5, X, Y).
X = 1,
Y = 4 ;
X = 2,
Y = 3 ;
X = 3,
Y = 2 ;
X = 4,
Y = 1 ;
Once you can get two, you can get a longer list by tearing one value off with decomp2/2 and getting the rest through induction. You just need to come up with a base case, such as, the singleton list of N:
decomp(N, [N]).
decomp(N, [X|L]) :- decomp2(N, X, Y), decomp(Y, L).
Be warned that this is going to produce a lot of repetition!
?- decomp(5, L).
L = [5] ;
L = [1, 4] ;
L = [1, 1, 3] ;
L = [1, 1, 1, 2] ;
L = [1, 1, 1, 1, 1] ;
L = [1, 1, 2, 1] ;
L = [1, 2, 2] ;
L = [1, 2, 1, 1] ;
L = [1, 3, 1] ;
L = [2, 3] ;
L = [2, 1, 2] ;
L = [2, 1, 1, 1] ;
L = [2, 2, 1] ;
L = [3, 2] ;
L = [3, 1, 1] ;
L = [4, 1] ;
You could probably clamp down on the repetition by introducing an ordering requirement, such as that X be greater than Y.
I have reached the solution and it looks something like this:
Remark: in isConsecutive i get rid of the "solution" when the list is the number itself
% equal with the given parameter N.
% generatePair(N - integer, X - integer, Y - integer)
% generatePair(i,o,o)
% generatePair(N) = { (X,Y), X<Y && X+Y=N
generatePair(N, X, Y) :-
my_between(1, N, Y),
my_between(1, N, X),
X < Y,
N =:= X + Y.
% This predicate decomposes the given number N into a list of integers
% such that their sum is equal to N.
% decomposeNumber(N - integer, L - list)
% decomposeNumber(i,o)
% decomposeNumber(N) = { [X|L]
decomposeNumber(N, [N]).
decomposeNumber(N, [X|L]) :- generatePair(N, X, Y), decomposeNumber(Y, L).
% This predicate checks it the that elements in the given list have
% consecutive value.
% isConsecutive(L - list)
% isConsecutive(i)
% isConsecutive([l1,l2,..,ln]) = { true, L=[l1,l2] && l1+1=l2
% { isConsecutive(l2..ln), l1+1=l2 && n>2
% { false, otherwise
isConsecutive([X,Y]):-X+1=:=Y.
isConsecutive([H1,H2|T]):-H2=:=H1+1, isConsecutive([H2|T]).
nAsSumOfConsecutives(N,L):-decomposeNumber(N,X), isConsecutive(X), L=X.
main(N,L):-findall(R,nAsSumOfConsecutives(N,R),L).
so I wrote a predicate that counts how many times an element occurs in a list of lists.
count([], _, 0). #base case
count([[Elem|Rest]|OtherLists], Elem, Count) :- #Elem is the head of sublist
!,
count([Rest|OtherLists], Elem, NewCount),
succ(NewCount, Count).
count([[_|Rest]|OtherLists], Elem, Count) :- #Elem is not the head of sublist
count([Rest|OtherLists], Elem, Count).
count([[]|OtherLists], Elem, Count) :- #Head sublist is an empty list
count(OtherLists, Elem, Count).
Now that if I query the predicate with the following:
count([[1,2,3],[4,1,5],[4,6,1]], 1, X).
it returns X = 3, which is correct, but it will also say 'false' if I continue with the query.
So it counts elements correctly, but I cannot use this predicate inside other predicates since it eventually returns FALSE.
What am I doing wrong?
When Prolog encounters a "choice point" (a place in the code where it can come back to seek more possible solutions) in the process of finding a solution, it will show the solution and prompt you for more possible solutions. If it finds no more, it displays "false". This is not any kind of error in your logic. It's the way Prolog works.
It is not always desirable to remove the choice point. It depends upon what your goals are for the predicate. The danger in removing choice points using cuts is that the choice point may be a path to valid alternative solutions, and the cut prevents your program from finding those solutions.
Let's try your updated program with the new proposed cut in your answer:
| ?- count([[1,2,3],[4,1,5],[4,6,1]], 1, X).
X = 3
yes
| ?- count([[1,2,1,3],[4,1,5],[4,6,1]], 1, X).
X = 4
yes
| ?- count([[1,2,1,3],[4,1,5],[4,6,1],[1]], 1, X).
X = 5
So far, so good. These look like complete and correct answers. I believe your additional cut (and including your original cut) will yield a correct answer as long as the first argument is fully bound with no variables. Let's try a more interesting query:
2 ?- count([[A,2,B],[C,1,D]], 1, X).
A = B, B = C, C = D, D = 1,
X = 5.
3 ?-
The predicate found one solution. However, aren't there more? What about this one?
A = _ % something other than 1
B = C, C = D, D = 1,
X = 4.
This would be a correct solution as well, but the predicate fails to find it.
Also, what about this query?
2 ?- count([[1,2,1,3],[4,1,5],[4,6,1],[1]], E, X).
E = 1,
X = 5.
3 ?-
Again, only one solution found. But aren't there more? What about E = 4 and X = 2?
If we remove all of the cuts from the original predicate in an attempt to get all of the correct solutions, then we get incorrect solutions as well:
2 ?- count([[1,2],[3,1,4],[1]], 1,X).
X = 3 ;
X = 2 ;
X = 2 ;
X = 1 ;
X = 2 ;
X = 1 ;
X = 1 ;
X = 0 ;
false.
2 ?- count([[1,2,1,3],[4,1,5],[4,6,1],[1]], E, X).
E = 1,
X = 5 ;
E = 1,
X = 4 ;
E = 1,
X = 3 ;
...
So if more generality is desired, a more effective solution needs to be constructed.
count_occurrences_lol([], _, 0).
count_occurrences_lol([List|Lists], X, Count) :-
count_occurrences(List, X, C1), % Count occurrences in this list
count_occurrences_lol(Lists, X, C2), % Count occurrences in remaining sublists
Count is C1 + C2. % Total the counts
count_occurrences([], _, 0).
count_occurrences([X|Xs], X, Count) :-
count_occurrences(Xs, X, C1),
Count is C1 + 1.
count_occurrences([X1|Xs], X, Count) :-
dif(X1, X),
count_occurrences(Xs, X, Count).
Now we get the following:
3 ?- count_occurrences_lol([[1,2],[3,1,4],[1]], 1,X).
X = 3 ;
false.
Just one solution, as expected. And the following:
5 ?- count_occurrences_lol([[A,2,B],[C,1,3]], 1, X).
A = B, B = C, C = 1,
X = 4 ;
A = B, B = 1,
X = 3,
dif(C, 1) ;
A = C, C = 1,
X = 3,
dif(B, 1) ;
A = 1,
X = 2,
dif(B, 1),
dif(C, 1) ;
B = C, C = 1,
X = 3,
dif(A, 1) ;
B = 1,
X = 2,
dif(A, 1),
dif(C, 1) ;
C = 1,
X = 2,
dif(A, 1),
dif(B, 1) ;
X = 1,
dif(A, 1),
dif(B, 1),
dif(C, 1) ;
false.
3 ?- count_occurrences_lol([[1,2,1,3],[4,1,5],[4,6,1],[1]], E, X).
E = 1,
X = 5 ;
E = 2,
X = 1 ;
E = 3,
X = 1 ;
E = 4,
X = 2 ;
E = 5,
X = 1 ;
E = 6,
X = 1 ;
X = 0,
dif(E, 1),
dif(E, 1),
dif(E, 6),
dif(E, 4),
dif(E, 5),
dif(E, 1),
dif(E, 4),
dif(E, 3),
dif(E, 1),
dif(E, 2),
dif(E, 1).
4 ?-
Several possible solutions as expected.
Ok, it looks like it was backtracking on the part where 'Elem is not the head of sublist', and I was able to fix it by changing it to:
count([[_|Rest]|OtherLists], Elem, Count) :- #Elem is not the head of sublist
!,
count([Rest|OtherLists], Elem, Count).
If anyone can confirm whether this is a correct solution. Thanks
I am to write a program that does this:
?- pLeap(2,5,X,Y).
X = 2,
Y = 3 ;
X = 3,
Y = 4 ;
X = 4,
Y = 5 ;
X = 5,
Y = 5 ;
false.
(gives all pairs X,X+1 between 2 and 5, plus the special case at the end).
This is supposedly the solution. I don't really understand how it works, could anyone guide me through it?
pLeap(X,X,X,X).
pLeap(L,H,X,Y) :-
L<H,
X is L,
Y is X+1.
pLeap(L,H,X,Y) :-
L=<H,
L1 is L+1,
pLeap(L1,H,X,Y).
I'd do it simply like this:
pLeap(L,H,X,Y) :-
X >= L,
X =< H,
Y is X+1.
Why doesn't it work (ignoring the special case at the end)?
You could use library clpfd for you problem.
:- use_module(library(clpfd)).
pLeap(L,H,X,Y) :-
X in L..H,
Y #= min(H, X+1),
label([X]).
Here is the output:
?- pLeap(2,5,X,Y).
X = 2,
Y = 3 ;
X = 3,
Y = 4 ;
X = 4,
Y = 5 ;
X = 5,
Y = 5.
The >= and =< operators don't instantiate their arguments, and you can only use them once the arguments have already been instantiated.
Put another way, in the given solution, X and Y are given values with is, and the < and =< operators are only used on L and H, whose values are given by the user. (On the given solution, try pLeap(L,H,2,3) and you'll get the same problem as you're having.)
In your case, though, you try to use >= and =< on X, which has no value yet, and so the interpreter complains.