List building problems - prolog

For my program I need to make a list of lists, with each sublist containing 2 numbers, X and Y along with the sum and product of these 2 numbers.
So far I have the following:
genList(95, X,[]):-!.
genList(N, X,[[X,Y,Sum,Product]|Xs]):-
Y is N+1,
Sum is X+Y,
Sum<101,
Product is X*Y,
N1 is N+1,
genList(N1, X,Xs).
This works just fine for my test case of genList(5,5,Q).
However, I'm having trouble making it work for any starting number.
The goal is to find every pair of numbers where sum<= 100. So running through the above for one starting value, X would find every pair 1 < X < Y, where sum<=100, and running through it with all numbers 2-N would give a complete list of possible pairs.
For those interested, the problem I'm working through is the sum/product problem, described here (Second on the page)
If anyone could help with this it would be greatly appreciated!
Also, no built in prolog predicates are able to be used, hence the complicated way of doing this rather than with a findall.
A small extract of the output produced by this predicated is as follows:
[[5,6,11,30],[5,7,12,35],[5,8,13,40],[5,9,14,45],[5,10,15,50],[5,11,16,55],[5,12,17,60],[5,13,18,65],[5,14,19,70],[5,15,20,75],[5,16,21,80],[5,17,22,85],[5,18,23,90],[5,19,24,95],[5,20,25,100],[5,21,26,105],[5,22,27,110], ...
EDIT:
Ok, so after some editing, here is the latest version of my code.
I think it's very close, but there's still something not quite right.
It cycles through number pairs, but requires the use of ";" to view all the answers, which isn't what I want. Additionally, it returns false after all the answers are exhausted. I just can't figure it out.
Also, it gives a complete answer in the middle, but then removes a sublist each time until I'm left with only the last set of pairs.
E.g. genList(0,48,48,Q). gives me:
[[48,49,97,2352],[48,50,98,2400],[48,51,99,2448],[48,52,100,2496]]
[[48,49,97,2352],[48,50,98,2400],[48,51,99,2448],[48,52,100,2496],[49,50,99,2450],[49,51,100,2499]]
[[48,49,97,2352],[48,50,98,2400],[48,51,99,2448],[49,50,99,2450],[49,51,100,2499]]
[[48,49,97,2352],[48,50,98,2400],[49,50,99,2450],[49,51,100,2499]]
[[48,49,97,2352],[49,50,99,2450],[49,51,100,2499]]
[[49,50,99,2450],[49,51,100,2499]]
false.
As you can see, a sublist gets removed each time, I just can't see why!

You can exploit Prolog backtracking here. Just state what you want. For example you could say:
I want X to be between 1 and 100.
I want Y to be between 1 and min(100 - X, X).
then I want their pair
Let's look at what a validPair/1 predicate would look like:
validPair(X-Y) :-
between(1, 100, X),
Limit is min(100 - X, X),
between(1, Limit, Y).
You can just call it with
?- validPair(X).
and browse results with ;, or build a list of all the matching pairs with findall/3.
Edit: even with recursion, we can keep our statements:
I want X to be between 1 and 100.
I want Y to be between 1 and min(100 - X, X).
then I want their pair
So, an idea to do it would be to set up a worker predicate:
validPair(Result) :-
validPair(0, 0, Result).
validPair(X, Y, R) :-
...
then set up the base case:
validPair(101, _Y, []) :- !.
and in the worker predicate, to implement the statements we made with some conditions:
validPair(X, Y, [SomeStuff|R]) :-
X =< 100,
Limit is min(100 - X, X),
Y =< Limit,
!,
% we can go on and increment Y once we're finished
validPair(X, NextY, R).
validPair(X, Y, R) :-
% if we come here that means that Y is finished growing and
% we have to increment X
NextX is X + 1,
validPair(NextX, 0, R).

I have a feeling you're tackling the problem the wrong way; I must admit I don't really understand what your predicate is doing.
The goal is to find every pair of numbers where sum<= 100.
Assuming you mean unordered pairs of non-negative integers, that's
between(0, 100, Sum),
between(0, Sum, X),
Y is Sum - X,
X =< Y.
The set of all such pairs (as a list) can then be constructed with findall/3.
You could also do this using CLP(fd):
use_module(library(clpfd)).
[X, Y, Sum] ins 0..100,
X #=< Y,
X + Y #= Sum,
label([X,Y,Sum]).

Related

Returning multiple values from a Prolog list

In this Prolog exercise, I'm trying to return the values from a list which are greater than a number N.
For example: greater_than([5,6,1,7], 5, X) should return X = 6 ; X = 7.
I tried to solve this by doing:
greater_than([],_,_). % to stop recursion when list is empty
greater_than([H|T],N,X) :-
H > N,
X is H,
greater_than(T,N,M). % if H>N return X=H
greater_than([H|T],N,X) :-
H =< N,
greater_than(T,N,X). % if H=<N just continue recursion.
My code works when there is only one result: greater_than([1,2,5], 2, X) returns X = 5.
But it doesn't work with multiple results: greater_than([1,2,5,7], 2, X) returns false.
I understood from this that X is already binded to a number and (X is H) for the second time returns false.
But I didn't know how to get multiple results.
I tried to change variables name:
greater_than([H|T],N,X) :-
H > N,
X is H,
greater_than(T,N,X1). % X1 for example
but that didn't work.
I understood from this that X is already binded to a number and (X is H) for the second time returns false.
Almost, but not quite because those happen in different calls so that could work on its own. Your code binds X=5 and then in the next call it binds M=7, and there's nowhere for you to see the value of M. The 7 is already used, when you search again, there's no more answers to find because it has found all the answers, reached the end of the list.
You are mixing up backtracking with recursion, two different ways of solving this.
A backtracking solution:
greater_than(List, Cutoff, X) :-
member(X, List),
X > Cutoff.
Then:
?- greater_than([1,2,5,7], 2, X).
X = 5 ;
X = 7
It finds one answer, and waits, then you ask for more, and it finds more.
A recursive solution walks the list in your code, instead of having Prolog do it, e.g. to build a list with all the answers:
greater_than([], _, []). % recursion base case. Empty list input, empty list output.
greater_than([H|T], Cutoff, [H|Result_T]) :-
H > Cutoff,
greater_than(T, Cutoff, Result_T).
greater_than([H|T], Cutoff, Result) :-
H =< Cutoff,
greater_than(T, Cutoff, Result).
Then:
?- greater_than([1,2,5], 2, X).
X = [5]

Switching arguments in Prolog

I have this little Prolog program. It multiplies two numbers only doing addition.
mult(0, _, 0).
mult(1, X, X).
mult(X, Y, R) :- X > 1, X1 is X - 1, mult(X1, Y, R1), R is Y+R1.
Now I thought of optimizing this by reordering the arguments, so that X is always the smaller of the two arguments and therefore doing less recursion, so I added this line:
mult(X, Y, R) :- X > Y, mult(Y, X, R).
This does not work and I don't really understand why. For example mult(3, 0, 0). answers with four times true and then false. I obviously just want to have it return true once and then false afterwards. There are also combinations which work fine like mult(0, 3, 0)..
Thanks in advance.
It works but yields redundant solutions.
The problem is that you have a single base case mult(0,_,0). that only check for the first argument to be 0, and not a base case that would give 0 whenever any of the first two arguments is 0. Also your second base case is really not needed.
When you add your "interchange arguments" clause you open the possibility of either interchange it when X > Y or just continue with the next clause, and that is why you end up with multiple solutions when the second argument was 0.
For example, the query mult(3,0, M). can either succeed by first exchanging 3 with 0, then calling mult(0,3, M). will succeed with M=0, o continue with the next clause that subtracts 1 to X and calls mult(2,0, M1). which again can yield a solution by first exchanging arguments or falling through the next clause until the first argument is 1 which guards against proceeding with the last clause.
You may fix it by renaming your procedure to something else and adding a wrapper which calls your procedure with the argument order of your like. e.g.:
mult(X, Y, R):-
X > Y -> mult1(Y, X, R) ; mult1(X, Y, R).
mult1(0, _, 0).
mult1(1, X, X).
mult1(X, Y, R) :- X > 1, X1 is X - 1, mult1(X1, Y, R1), R is Y+R1.

Predicate in prolog

I need to define a predicate in prolog which takes a list as input and sums the squares of numbers >= 5 and subtract sum of absolute value of numbers <=2.
This is what I have currently :-
pred([], 0).
pred([Head|Tail], Result) :-
gr85(Head),
pred(Tail, Total),
Result is Head*Head + Total.
pred([Head|Tail], Result) :-
leq2(Head),
pred(Tail, Total),
Result is Total - Head.
gr85(Number):-
Number >= 5.
leq2(Number):-
Number =< 2.
My question is how do I exclude anything between 2 and 5. If I input 3 in the list, it returns false.
Expected input
pred([3,6,2,-1], Result).
Expected output
Result= 33 (6*6 -2-1)
add a 'skip all' clause:
weird_sum([_|Tail], Result) :- weird_sum(Tail, Result).
I think you will gain some insight into Prolog working when you analyze where this clause should be added.
Maybe it is simpler if you keep it more simple, you can use accumulator to make it simpler. And also it is maybe less typing if you move the condiditional out, like this;
weird_sum(List, Sum) :-
weird_sum(List, 0, Sum).
weird_sum([], Acc, Sum) :- Sum is Acc.
weird_sum([X|Xs], Sum0, Sum) :-
once( weird_sum_helper(X, Sum0, Sum1) ),
weird_sum(Xs, Sum1, Sum).
weird_sum_helper(X, Acc, Acc + X*X) :- X >= 5.
weird_sum_helper(X, Acc, Acc - abs(X)) :- X =< 2.
weird_sum_helper(X, Acc, Acc) :- X < 5, X > 2.
But the actual answer to your original question is in the last line above, just say that when X is smaller than 5 AND X is larger than 2 than you don't do anything.
But I get different answer from 33 because when number is negative and you subtract you get positive not negative but I am not sure if this is what you mean.
I used once once just because you want to do this only once since the three conditions are excluding each other but somehow Prolog doesn't know that it should only take one and tries the others too.

prolog - confusing behaviour of prolog code (beginner)

Here is my code.
equals2(X,Y,N,I):- X is Y,I is N+1; I is N.
elemNum(X,[],0).
elemNum(X,[Y|Ys],N) :- elemNum(X,Ys,N1),equals2(X,Y,N1,I),N is I.
lemNum first argument is element from array, second is array. It counts the number of elements in array.
Then in console
| ?- elemNum(1,[1,2,3,1,1],N),N<2.
N = 1 ?
yes
I am sure than my function elemNum works just fine. How its possible that in console this assertion returns 1?
Thanks for help
Non sure to understand what do you want ... but I suppose that you want count the number of element in the list (second argument of elemNum/3) that are equals to the first argument.
If so, you should modify equals2/4 as follows
equals2(X,Y,N,I):- X is Y,I is N+1; X \== Y, I is N.
or better (IMHO) split it in 2 different clauses
equals2(X,X,N,I):- I is N+1.
equals2(X,Y,N,N):- X \== Y.
With your equal2/4, the second or case (I is N) is executed (in backtracking) even when X is equal to Y so elemNum(1,[1,2,3,1,1],N) unifiy N with 3, 2, 2 again, 1, 2, 1, 1 again and 0.
Regarding elemNum/3, works but you can semplify it (avoiding a warning) as
elemNum(_,[],0).
elemNum(X,[Y|Ys],I) :- elemNum(X,Ys,N1), equals2(X,Y,N1,I).
or you can rewrite it, avoiding the use of equals2/4 as
elemNum(_, [], 0).
elemNum(X, [X | Ys], I) :- elemNum(X, Ys, I0), I is I0+1.
elemNum(X, [Y | Ys], I) :- X \== Y, elemNum(X, Ys, I).

Generate list of numbers to fit certain criteria

I had a predicate to generate a list of numbers,
generate_numbers(0,[]).
generate_numbers(N,[Head|Tail]):-
N > 0,
Head is N,
N1 is N-1,
generate_numbers(N1,Tail).
I am trying to modify it so that it generates numbers X and Y up to a limit. The conditions are that 1 < X < Y and S = X + Y and S < 100.
I'm struggling to work out how to do it, I've tried but it's nowhere near correct.
generate_numbers(1,[]).
generate_numbers(X,[[Head,Y,S]|Tail]):-
X > 1,
Head is X,
Y is X+1,
S is X + Y,
X1 is X-1,
generate_numbers(X1,Tail).
I did have a check for S > 100 but that stopped the code from working at all.
The output I'm after would be along the lines of [1,2,3], [2,4,6], [3,9,12], [20,25,45]. Obviously that's just a few examples, there will be 1000s in reality.
I think that I might need to recurse twice. So add 1 to X then keep adding 1 to Y until the limit is reached, add 1 to X and keep adding 1 to Y again until the limit is reached and keep doing this recursively. This should make sure that every possible pair is created.
There are a couple of ways to approach this. The brute force way is to have a second layer of base case to iterate over one of the two addends in the summation. This solution will provide the list first in order of total sum, then by value of X:
gen_numbers(MaxSum, Result) :-
MaxSum > 1,
gen_numbers(2, MaxSum, 1, Result).
gen_numbers(Sum, Sum, Sum, []). % 'Sum' has reached max value
gen_numbers(Sum, MaxSum, Sum, Result) :- % 'X' has reached max value
Sum < MaxSum,
Sum1 is Sum + 1,
gen_numbers(Sum1, MaxSum, 1, Result).
gen_numbers(Sum, MaxSum, X, [[X, Y, Sum]|Result]) :-
Sum =< MaxSum,
X < Sum,
Y is Sum - X,
X1 is X + 1,
gen_numbers(Sum, MaxSum, X1, Result).
By counting up instead of down, I could keep the list in forward order and maintain tail recursion without the use of an auxiliary list. I constrained by X and the Sum and let Y vary. I think you could easily modify this to vary based upon whatever variables you wish.
A cleaner approach is to use the CLPFD library and specify the conditions as constraints:
:- use_module(library(clpfd)).
summation(Sum, [X, Y, S]) :-
[X, Y] ins 1..Sum,
sum([X, Y], #=<, Sum),
label([X, Y]),
S is X + Y.
gen_numbers(MaxSum, Result) :-
findall([X, Y, S], summation(MaxSum, [X, Y, S]), Result).
Here, summation/2 presents one of each solution at a time, and findall/3 collects them. This solution is easily scalable to support more addends.

Resources