Generate list of numbers to fit certain criteria - prolog

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.

Related

Prolog - print the sum of the EVEN numbers from 1 to 10

I am trying to print all the even numbers from 1 to 10 using Prolog, and here is what I have tried:
printn(10,0):- write(10),!.
printn(X,Sum):-
( X mod 2 =:= 0 -> Sum is X+Sum, Next is X+1, nl, printn(Next);
Next is X+1, printn(Next) ).
but it returns false.
You don't need to create the list with the numbers from the beginning, it is better to examine numbers once:
print(X,Y):-print_even(X,Y,0).
print_even(X, X, Sum):-
( X mod 2 =:= 0 -> Sum1 is X+Sum;
Sum1 = Sum
), print(Sum1).
print_even(X, Y, Sum):-
X<Y, Next is X+1,
( X mod 2 =:= 0 -> Sum1 is X+Sum, print_even(Next, Y, Sum1);
print_even(Next, Y, Sum)
).
Keep in mind that in Prolog Sum is Sum+1 always fails you need to use a new variable e.g Sum1.
Example:
?- print(1,10).
30
true ;
false.
The most useful way of obtaining Prolog output is to capture the solution in a variable, either individually through backtracking, or in a list. The idea of "printing", which carries over from using other languages allows for formatting, etc, but is not considered the best way to express a solution.
In Prolog, you want to express your problem as a relation. For example, we might say, even_with_max(X, Max) is true (or succeeds) if X is an even number less than or equal to Max. In Prolog, when reasoning with integers, the CLP(FD) library is what you want to use.
:- use_module(library(clpfd)).
even_up_to(X, Max) :-
X in 1..Max,
X mod 2 #= 0, % EDIT: as suggested by Taku
label([X]).
This will yield:
3 ?- even_up_to(X, 10).
X = 2 ;
X = 4 ;
X = 6 ;
X = 8 ;
X = 10.
If you then want to collect into a list, you can use: findall(X, even_up_to(X), Evens).
What error do you have? Here is my solution:
Create list [1...10]
Filter it, excluding odd numbers
Sum elements of the list
Code:
sumList([], 0).
sumList([Head|Tail], Sum) :-
sumList(Tail, Rest),
Sum is Head + Rest.
isOdd(X) :-
not((X mod 2) =:= 0).
sumOfEvenNums(A, B, Out) :-
numlist(A, B, Numbers),
exclude(isOdd, Numbers, Even_numbers),
sumList(Even_numbers, Out).
Now you can call sumOfEvenNums(1, 10, N)
In ECLiPSe, you can write with iterator:
sum_even(Sum):-
( for(I,1,10),fromto(0,In,Out,Sum)
do
(I mod 2 =:= 0 -> Out is In + I;Out is In)
)
With library(aggregate):
evens_upto(Sum) :-
aggregate(sum(E), (between(1, 10, E), E mod 2 =:= 0), Sum).
Thanks to #CapelliC for the inspiration.

Find minimum of various predicates

I want to find the minimum value of all permutations called from main predicate. For simplicity, I have removed my entire code, assume that I just want to find the minimum of head elements of all permutations.
appendlist([], X, X).
appendlist([T|H], X, [T|L]) :- appendlist(H, X, L).
permutation([], []).
permutation([X], [X]) :-!.
permutation([T|H], X) :- permutation(H, H1), appendlist(L1, L2, H1), appendlist(L1, [T], X1), appendlist(X1, L2, X).
%min(X, A, B) X is the minimum of A, B
min(X, X, Y) :- X =< Y.
min(Y, X, Y) :- Y < X.
solve([Head|Rest], Head):-
writeln([Head|Rest]).
main :-
Sort = [1, 2, 3],
PrvAns is 1000,
permutation(Sort, X),
solve(X, Here),
min(Ans, Here, PrvAns),
writeln(Ans),
PrvAns = Ans,
!, fail;
true,
writeln(PrvAns).
I want to calculate the minimum on fly for each permutation. Now, permute is working fine, and you can see that solve prints all permutations and even returns the first value Head properly, but PrvAns = Ans is wrong.
Expected output PrvAns : 1
I'm sorry if I didn't understand properly (and tell me, so I can help you), but, you mean something like this?
findMinHead(X,Z):-
findall( Y, ( permutation(X,[Y|_]) ), Z1 ),
min_list(Z1,Z).
in this predicate we find all the Y values where Y is the head of a permutation of X, put all that values in a bag, and then find the min.

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).

More trouble with prolog list building

I'm sorry to post another question on this, but I just seem to be going in circles here!
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(100,N, X,[]).
genList(S,N, X,[[X,Y,Sum,Product]|Xs]):-
Y is N+1,
Sum is X+Y,
NewS is Sum,
Sum<101,
Product is X*Y,
N1 is N+1,
genList(NewS,N1, X,Xs).
genList(S,N,X,Q):-
N+X < 101,
NextX is X + 1,
genList(0,NextX,NextX,Q).
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 predicate 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], ...
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 for the starting value, 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!
Well, you're almost there. Since you spent quite some time on this problem already I'll just show you some valid code and comment it:
First we call a worker predicate that'll carry X and Y as arguments and initialize them to 0:
validPair(Result) :-
validPair(0, 0, Result).
Then we handle our base case. Since we started from 0, the base case is the upper bound. We could have went the other way around, it's just a choice really. Note that the cut here means that we won't have to worry about Y being superior to 100 in our following clauses since they won't be executed in that case.
validPair(_X, 101, []) :- !.
Here is now the case where X fits the correct limits for the sum to be under 100. We first check that everything is ok and then we use the !/0 predicate to once more prevent the execution to reach our last clause since that wouldn't make sense. After it's done we just avec to calculate the interesting values and add them to the list.
validPair(X, Y, [[X, Y, Sum, Product]|R]) :-
Limit is min(100 - Y, Y),
X =< Limit,
!,
Sum is X + Y,
Product is X * Y,
NextX is X + 1,
validPair(NextX, Y, R).
The only case left to handle is when X goes above the limit we fixed so that the sum is under 100. When that happens we start again with the next Y and reset X to 0.
validPair(_X, Y, R) :-
NextY is Y + 1,
validPair(0, NextY, R).
If anything troubles you please ask for clarification in comments.
Note: the cuts used here are red cuts - ie the correctness of the predicate totally depends on the order of the clauses. It's bad practise. Try to supplement them with proper guards (like X =< 100 for example), it'd be a good addition :)
Edit:
Now let's audit your code :D
I'll start by commenting on the style:
In this clause (called fact since it has no body), you use N and X only once ie you do not care about storing their value. In this case, we prefix their name with a _ or just use the anonymous variable _:
genList(100,N, X,[]).
turns into
genList(100, _N, _X, []).
or
genList(100, _, _, []).
Same thing here with S. It's not used in this clause. It's only used in the first one. You can replace it by _ or _Sum if you want to document its use in the other clause here too (good practise). Then, you use two variables to hold the exact same value. It has no interest here. Just call your next genList/4 with Sum as first argument instead of declaring a new variable just for that. Same thing with Y and N1. A proper version of this clause turns:
genList(S,N, X,[[X,Y,Sum,Product]|Xs]):-
Y is N+1,
Sum is X+Y,
NewS is Sum,
Sum<101,
Product is X*Y,
N1 is N+1,
genList(NewS,N1, X,Xs).
into
genList(_PreviousSum, N, X,[[X, Y, Sum, Product]|Xs]):-
Y is N+1,
Sum is X + Y,
Sum<101,
Product is X * Y,
genList(Sum, Y, X, Xs).
Your last clause has an issue with arithmetic: N + X is just the term +(N, X). It's not the value N + X. You have to use the is/2 predicate just as in your other clauses. Same issue as the second clause for S. Those little edits turn:
genList(S,N,X,Q):-
N+X < 101,
NextX is X + 1,
genList(0,NextX,NextX,Q).
into
genList(_PreviousSum, N, X, Q) :-
Sum is N + X,
Sum < 101,
NextX is X + 1,
genList(0, NextX, NextX, Q).
So, at the moment your corrected program looks like:
genList(100, _N, _X, []).
genList(_PreviousSum, N, X,[[X, Y, Sum, Product]|Xs]):-
Y is N+1,
Sum is X + Y,
Sum<101,
Product is X * Y,
genList(Sum, Y, X, Xs).
genList(_PreviousSum, N, X, Q) :-
Sum is N + X,
Sum < 101,
NextX is X + 1,
genList(0, NextX, NextX, Q).
Since it was just style edits, it doesn't change its behavior.
Now let's look at what was wrong about it, not in the style, but in the logic.
First, the base case. Here everything is fine. You check for the sum being your upper bound and if it is return []. Perfect!
genList(100, _N, _X, []).
Now, your "inner recursion". It's almost fine. Let's look at the detail that bugs me: you have a value that holds your previous sum, but compute a new one and retest it against the upper bound + 1. a better idea would be to test PreviousSum against < 100 and to remove the Sum < 101 test. It'd better justify the fact that you have an argument just for that! Plus it's more understandable that it's guard being used to prevent execution of the clause in that limit case. So,
genList(_PreviousSum, N, X,[[X, Y, Sum, Product]|Xs]):-
Y is N+1,
Sum is X + Y,
Sum<101,
Product is X * Y,
genList(Sum, Y, X, Xs).
would turn into
genList(PreviousSum, N, X,[[X, Y, Sum, Product]|Xs]):-
PreviousSum < 100,
Y is N+1,
Sum is X + Y,
Product is X * Y,
genList(Sum, Y, X, Xs).
Note that this modification is kinda stylistic, it doesn't modify the program behavior. It's still makes it way more readable though!
Now, the big bad wolf:
genList(_PreviousSum, N, X, Q) :-
Sum is N + X,
Sum < 101,
NextX is X + 1,
genList(0, NextX, NextX, Q).
Many things to say here. First and once again, you do not need to compute Sum since PreviousSum already holds the value. Then, it should be tested for < 100 instead of < 101. Then, it should be tested that X >= N, because that's only in that case that you do not want to go through your second clause but in this one instead. And last but not least, instead of starting a new iteration with genList(0, NextX, NextX, Q) you should start it with genList(NextX, 0, NextX, Q). Here you didn't reset properly the values. The resulting clause is:
genList(PreviousSum, N, X, Q) :-
PreviousSum < 100,
N >= X,
NextX is X + 1,
genList(NextX, 0, NextX, Q).
As you saw, we know formalized that we can't go through our second clause if N >= X. We should add to it the proper test to be assured that it's correct:
genList(PreviousSum, N, X,[[X, Y, Sum, Product]|Xs]):-
PreviousSum < 100,
N < X,
Y is N+1,
Sum is X + Y,
Product is X * Y,
genList(Sum, Y, X, Xs).
Here you're done, your program is correct!
Final version is:
genList(100, _N, _X, []).
genList(PreviousSum, N, X,[[X, Y, Sum, Product]|Xs]):-
PreviousSum < 100,
N < X,
Y is N+1,
Sum is X + Y,
Product is X * Y,
genList(Sum, Y, X, Xs).
genList(PreviousSum, N, X, Q) :-
PreviousSum < 100,
N >= X,
NextX is X + 1,
genList(NextX, 0, NextX, Q).
The variable naming is still poor. A clearer version would be:
genList(100, _X, _Y, []).
genList(PreviousSum, X, Y,[[X, Y, Sum, Product]|Xs]):-
PreviousSum < 100,
X < Y,
NewX is X + 1,
Sum is X + Y,
Product is X * Y,
genList(Sum, NewX, Y, Xs).
genList(PreviousSum, X, Y, Q) :-
PreviousSum < 100,
X >= Y,
NextY is Y + 1,
genList(NextY, 0, NextY, Q).
Here you still have a problem (yeah it nevers ends :D): the fact that you increment your variables BEFORE calculating the sum product etc means that you skip some values. Try to increment after. It's let as an exercise :)

List building problems

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]).

Resources