I was challenged with a CS problem.
The problem consists of recursively finding which expressions of the form ((10+10)/(10+10)) produces a number. For example, ((10+10)/(10+10)) produces 1. Find all the other expressions using the operators +, -, *, /, with 4 numbers of 10, and all the combinations of parentheses to enforce orders of operations.
I was referred to the Reverse Polish Notation, but that relies on postfix notation, which isn’t required to solve this problem.
Some pseudocode I have is this. I know using recursion is the easiest way to solve this problem. But don't know how to make sure I get all combinations.
build([10,10,10,10], Expression) :-
Operator
/ \
[10] [10,10,10]
Operator
/ \
[10] [10,10]
Operator
/ \
[10] [10]
This is a problem I am trying to solve in Prolog but C++ is good as well.
I have a partial solution which I will outline here and hopefully it will get you moving and you can find the complete solution.
The first tool you need is the ability to make some expressions:
build_expr(X, Y, X+Y).
build_expr(X, Y, X*Y).
build_expr(X, Y, X-Y).
build_expr(X, Y, X/Y).
This defines build_expr/3, which takes two variables or expressions and produces a new expression. This is how we are going to permute the operators. Now we need a way to handle the lists, so let's define build_expr/2 that operates on a list at once:
% base case: we are down to two variables and call build_expr/3
build_expr([X,Y], Expr) :- build_expr(X, Y, Expr).
% inductive case: make the expression on the rest of the list and combine
% with the leading variable here
build_expr([X|Rest], Expr) :-
build_expr(Rest, Expr0),
build_expr(X, Expr0, Expr).
Let's get a few solutions so we get the flavor of what it's doing:
3 ?- build_expr([10,10,10,10],X).
X = 10+(10+(10+10)) ;
X = 10*(10+(10+10)) ;
X = 10-(10+(10+10)) ;
X = 10/(10+(10+10)) ;
X = 10+10*(10+10) ;
X = 10*(10*(10+10)) ;
X = 10-10*(10+10) ;
X = 10/(10*(10+10)) ;
X = 10+(10-(10+10)) ;
X = 10*(10-(10+10)) ;
X = 10-(10-(10+10)) ;
X = 10/(10-(10+10)) ;
This looks pretty good to me. But like I said, I'm only generating the right-leaning tree. You will have to modify or replace build_expr/2 to produce the other shapes, if they actually matter (which I'm not convinced they do).
Now let's make the next step simpler by bundling in evaluation:
build_eval(L, Value) :- build_expr(L, Expr), Value is Expr.
Now we should be able to find all the unique solutions using setof/3:
6 ?- setof(X, build_eval([10,10,10,10],X), Results).
ERROR: Arithmetic: evaluation error: `zero_divisor'
ERROR: In:
ERROR: [15] _582 is 10/(10* ...)
ERROR: [14] build_eval([10,10|...],_622) at /Users/dlyons/fourtens.pl:11
ERROR: [13] '$bags':findall_loop(_664,user:build_eval(...,_682),_668,[]) at /usr/local/Cellar/swi-prolog/7.6.4/libexec/lib/swipl-7.6.4/boot/bags.pl:97
ERROR: [12] setup_call_catcher_cleanup('$bags':'$new_findall_bag','$bags':findall_loop(_728,...,_732,[]),_710,'$bags':'$destroy_findall_bag') at /usr/local/Cellar/swi-prolog/7.6.4/libexec/lib/swipl-7.6.4/boot/init.pl:443
ERROR: [8] '$bags':setof(_770,user:build_eval(...,_786),_774) at /usr/local/Cellar/swi-prolog/7.6.4/libexec/lib/swipl-7.6.4/boot/bags.pl:240
ERROR: [7] <user>
ERROR:
ERROR: Note: some frames are missing due to last-call optimization.
ERROR: Re-run your program in debug mode (:- debug.) to get more detail.
ERROR: [13] '$bags':findall_loop(_664,user:build_eval(...,_682),_668,[]) aabort
% Execution Aborted
Oops. Division by zero error. No problem, let's catch that and fail in those cases instead:
9 ?- setof(X, catch(build_eval([10,10,10,10],X), E, fail), Results), writeln(Results).
[-990,-900,-190,-100,-80,-20,-1,-0.1111111111111111,
0,0.01,0.05,0.09090909090909091,0.3333333333333333,1.0,1,
5.0,9.5,9.9,10,10.1,10.5,20.0,20,40,100.0,100,
120,210,300,1010,1100,2000,10000]
I fiddled with the formatting there a little, but I think that's a pretty good solution, but I can already see one missing solution: (10+10)*(10+10)=400. So you will have to get more creative with build_expr/2 to make it produce other shapes of tree.
Edit: Adding the rest of the solutions
I found an answer by #gusbro that gives a way to enumerate the trees. I wasn't able to get it to work with the recursive trickery I was doing there (maybe someone else will show me a very easy trick) but I was able to adapt his answer to your problem, to wit:
build_tree([I1,I2|Items], Expr) :-
append([L0|LR], [R0|RR], [I1,I2|Items]),
build_tree([L0|LR], Left),
build_tree([R0|RR], Right),
build_expr(Left, Right, Expr).
build_tree([E], E).
Why am I using [L0|LR] and [R0|RR] instead of LeftList and RightList or some such? This is how I'm turning #gusbro's numeric constraints into list length constraints and ensuring that I always have at least one element in both the left and right lists, so my recursive calls to build_tree/2 will succeed.
Simplifying build_expr/3 from above down to a single operator you can see this generates all the various flavors you'd expect:
?- build_tree([10,10,10,10],X).
X = 10+(10+(10+10)) ;
X = 10+(10+10+10) ;
X = 10+10+(10+10) ;
X = 10+(10+10)+10 ;
X = 10+10+10+10 ;
false.
Switch it back, because we're still using the build_expr/3 function from the earlier example. I have simplified the evaluation somewhat using this build_eval/2 predicate:
build_eval(L, Value) :-
build_tree(L, Expr), catch(Value is Expr, _, fail).
Here's what the final solution looks like:
?- setof(X, build_eval([10,10,10,10], X), Res), writeln(Res).
[-990,-900,-190,-100,-99,-90,-80,-20,-19,-10,-9.9,-9.5,-9,
-8,-1.1111111111111112,-1,-0.9,-0.1111111111111111,
0,0.01,0.05,0.09090909090909091,0.1111111111111111,
0.2,0.3333333333333333,0.9,0.9090909090909091,1.0,1,
1.1,1.1111111111111112,2,3,5.0,5,8,9,9.5,9.9,10,10.1,10.5,11,
12,19,20.0,20,21,40,80,90,99,100.0,100,101,110,120,190,
200,210,300,400,900,990,1010,1100,2000,10000]
Wow, quite a few alternatives, 68 to be exact!
Related
I'm trying to write a function that recursively adds the first two items in a List, and returns when one item remains.
Example:
addList([1,2,3,4], X)
x = [10]
(Steps here would be: [1,2,3,4] -> [3,3,4] -> [6,4] -> [10] )
This is what I have:
addList([],[]).
addList([H|[H2|T]], []) :- L is H+H2, addList(T, [L|T]).
addList([H|T], [H2|_]) :- L is H+H2, addList(T, [L|T]).
In my mind, this would do something like the following:
addList([1,2,3,4], X).
L=1+2 --> addList([3,4], [3,3,4])
L=3+3 --> addList([4], [6, 4])
L=6+4 --> addList([], [10])
This actually causes an error - "Arguments are not sufficiently instantiated"
If I make the first addList into addList([],[_])., it'll output X = [] . first, then have the same error if I use ; to continue.
It should end in false.
(After having the chain of X = [1,2,3,4] ; X = [3,3,4] ; X = [6,4] ; X = [10] ; false.
First, note that you can write [H|[H2|T]] equivalently as [H,H2|T].
Also, as far as I know in Prolog the preferred style is to use snake_case instead of camelCase.
add_list([H1,H2|T], []) :- ...
In the above clause, you already unifiy the second term with the empty list, and what happens in ... is just a way of checking if the relationship holds. The L that occurs in the body is only a local variable; likewise, the recursive call to add_list is not used to compute the "result" (the second parameter).
You expect the result to always be a singleton list containing the total sum of your values, except if the list is empty (in which case the result is empty). The base cases are then:
add_list([], []).
add_list([N], [N]).
The general case is necessarily something like:
add_list([H1,H2|T], [Sum]) :- ...
And you have to expression the recursive relationship in terms of H1, H2, T and Sum. In fact until you reach a base case you are likely to only pass down the second argument unmodified, so you do not need to write [Sum] explicitly, you could just write Res:
add_list([H1,H2|T], Res) :-
...,
add_list(..., Res).
New to prolog and trying to implement the following function that takes 3 lists:
True if lists are the same length
True if elements of third list is sum of the two lists
Example: fn([1,2,3],[4,5,6],[5,7,9]) returns true. Note that the sum is element-wise addition.
This is what I have so far:
fn([],[],[]).
fn([_|T1], [_|T2], [_|T3]) :-
fn(T1,T2,T3), % check they are same length
fn(T1,T2,N1), % check that T3=T1+T2
N1 is T1+T2,
N1 = T3.
From what I understand, the error is due to the base case (it has empty lists which causes error with evaluation of addition?)
Thanks for any help and explanations!
In addition to #GuyCoder's answer, I would point out that it is worthwhile to consider using one of the maplist predicates from library(apply) when modifying all elements of lists. You can use a predicate to describe the relation between three numbers...
:- use_module(library(apply)). % for maplist/4
num_num_sum(X,Y,S) :-
S is X+Y.
... and subsequently use maplist/4 to apply it to entire lists:
fn(X,Y,Z) :-
maplist(num_num_sum,X,Y,Z).
This predicate yields the desired results if the first two lists are fully instantiated:
?- fn([1,2,3],[4,5,6],X).
X = [5,7,9]
However, due to the use of is/2 you get instantiation errors if the first two lists contain variables:
?- fn([1,A,3],[4,5,6],[5,7,9]).
ERROR at clause 1 of user:num_num_sum/3 !!
INSTANTIATION ERROR- X is _+B: expected bound value
?- fn([1,2,3],[4,5,A],[5,7,9]).
ERROR at clause 1 of user:num_num_sum/3 !!
INSTANTIATION ERROR- X is A+B: expected bound value
If you only want to use the predicate for lists of integers, you can use CLP(FD) to make it more versatile:
:- use_module(library(apply)).
:- use_module(library(clpfd)). % <- use CLP(FD)
int_int_sum(X,Y,S) :-
S #= X+Y. % use CLP(FD) constraint #=/2 instead of is/2
fnCLP(X,Y,Z) :-
maplist(int_int_sum,X,Y,Z).
With this definition the previously problematic queries work as well:
?- fnCLP([1,A,3],[4,5,6],[5,7,9]).
A = 2
?- fnCLP([1,2,3],[4,5,A],[5,7,9]).
A = 6
Even the most general query yields results with this version:
?- fnCLP(X,Y,Z).
X = Y = Z = [] ? ;
X = [_A],
Y = [_B],
Z = [_C],
_A+_B#=_C ? ;
X = [_A,_B],
Y = [_C,_D],
Z = [_E,_F],
_A+_C#=_E,
_B+_D#=_F ? ;
.
.
.
Since the numbers in the above answers are not uniquely determined, you get residual goals instead of actual numbers. In order to get actual numbers in the answers, you have to restrict the range of two of the lists and label them subsequently (see documentation for details), e.g. to generate lists containing the numbers 3,4,5 in the first list and 6,7,8 in the second list, you can query:
label the lists
restrict the domain | |
v v v v
?- fnCLP(X,Y,Z), X ins 3..5, Y ins 6..8, label(X), label(Y).
X = Y = Z = [] ? ;
X = [3],
Y = [6],
Z = [9] ? ;
X = [3],
Y = [7],
Z = [10] ? ;
.
.
.
X = [3,4],
Y = [6,7],
Z = [9,11] ? ;
X = [3,4],
Y = [6,8],
Z = [9,12] ? ;
.
.
.
On an additional note: there are also clp libraries for booleans (CLP(B)), rationals and reals (CLP(Q,R)) that you might find interesting.
From what I understand, the error is due to the base case.
I don't see it that way.
The first problem I see is that you are trying to process list which leads to thinking about using DCGs, but since you are new I will avoid that route.
When processing list you typically process the head of the list then pass the tail back to the predicate using recursion.
e.g. for length of list you would have
ln([],N,N).
ln([_|T],N0,N) :-
N1 is N0+1,
ln(T,N1,N).
ln(L,N) :-
ln(L,0,N).
The predicate ln/2 is used to set up the initial count of 0 and the predicate ln/3 does the work using recursion. Notice how the head of the list is taken off the front of the list and the tail of the list is passed recursively onto the predicate again. When the list is empty the predicate ln([],N,N). unifies, in this case think copies, the intermediate count from the second position into the third position, which it what is passed back with ln/2.
Now back to your problem.
The base case is fine
fn([],[],[]).
There are three list and for each one look at the list as [H|T]
fn([H1|T1],[H2|T2],[H3|T3])
and the call to do the recursion on the tail is
fn(T1,T2,T3)
all that is left is to process the heads which is
H3 is H1 + H2
putting it all together gives us
fn([],[],[]).
fn([H1|T1], [H2|T2], [H3|T3]) :-
H3 is H1 + H2,
fn(T1,T2,T3).
and a quick few checks.
?- fn([],[],[]).
true.
?- fn([1],[1],[2]).
true.
?- fn([1,2],[3,4],[4,6]).
true.
?- fn([1,2],[3,4,5],[4,6,5]).
false.
With regards to the two conditions. When I look at exercises problems for logic programming they sometimes give a condition like True if lists are the same length or some other condition that returns true. I tend to ignore those at first and concentrate on getting the other part done first, in this case elements of third list is sum of the two lists then I check to see if the other conditions are correct. For most simple classroom exercises they are. I sometimes think teacher try to give out these extra conditions to confuse the student, but in reality the are there just to clarify how the code should work.
I am new to prolog and I was wondering if anyone could help me with this problem. The problem: given the integers 1,2,3,4, and the predicates mult/2, div/2, div/2, minus/2, and minus/2, and eval/2, I need to write a predicate solutions/1 that, when called like this:
?- solutions(L).
it should terminate with the variable L unified to a list of expressions with value 6. Expressions are of the form:
X, Y, exp/2
But my code is not working. I have two versions. The first freezes up SWI-Prolog, not returning any answer after I type a period, and not letting me evaluate anything else afterward:
eval(1,1.0).
eval(2,2.0).
eval(3,3.0).
eval(4,4.0).
eval(mult(X,Y),Z) :-
eval(X,A),
eval(Y,B),
Z is A*B.
eval(div(X,Y),Z) :-
eval(X,A),
eval(Y,B),
Z is A/B.
eval(minus(X,Y),Z) :-
eval(X,A),
eval(Y,B),
Z is A-B.
solutions(L) :-
setof(X,eval(X,6),L),
print(L).
The second version just returns false when I type ?- solutions(L).:
solutions(L) :-
setof([exp,X,Y],eval(exp(X,Y),6),L),
print(L).
Thank you so much for taking the time to help!
Maybe you're looking for something like
solutions(L) :-
Ns = [1,2,3,4],
Ex = [*,/,-],
findall((X,Y,E),
(member(X,Ns),member(Y,Ns),member(E,Ex),F=..[E,X,Y],6=:=F),
L).
that yields
?- solutions(L).
L = [(2, 3, (*)), (3, 2, (*))].
Expressions are usually recursive, that is, arguments could be expressions instead of plain numbers. But then, in my opinion your problem is underspecified, as we need criteria to stop the infinite flow of solutions resulting - for instance - by repeated application of operations that don't change the value. Like multiply or divide by 1.
The problem is that your code is going in infinite recursion with eval/2 predicate.
You can try this solution:
num(1).
num(2).
num(3).
num(4).
eval(mult(A,B),Z) :-
num(A),
num(B),
Z is A*B.
eval(div(A,B),Z) :-
num(A),
num(B),
Z is A/B.
eval(minus(A,B),Z) :-
num(A),
num(B),
Z is A-B.
test(L) :-
setof(X,eval(X,6),L),
print(L).
Which yields:
?- test(L).
[mult(2,3),mult(3,2)]
L = [mult(2, 3), mult(3, 2)].
I am trying to learn Prolog. I have a problem and the solution for that in Prolog. Though I am unable to understand the code completely.
The problem is -
Write a procedure mydelete( X, HasXs, OneLessXs ) that returns
% ?- mydelete( 2, [1,2,3,4], L ) . --> L = [1,3,4]
% ?- mydelete( 2, [1,2,3,2], L ) . --> L = [1,3,2] ; L = [1,2,3]
Basically, the problem is t remove the member one by one which matches X and print the result after each removal.
I have a solution , but, I am exactly, not sure how this code is working.
mydelete(X,[X|T],T).
mydelete(X,[H|T1],[H|T2]) :- mydelete(X,T1,T2).
As per my understanding, the first line, displays the L = ... when it finds a match with X in the head of the list.
In the second line of the code, it simply pops out the head from the input list and send that updated list recursively.
But, here, we haven't defined T2.
Let us consider an example for that.
mydelete( 2, [1,2,3,4], L ) . --> this is the call.
X=2, list is = [1,2,3,4], so, H=1, T=[2,3,4].
So, it does not execute line 1 of the code. Now, it comes to the second line of the code.
mydelete(X,[H|T1],[H|T2]) :- mydelete(X,T1,T2).
Here also X=2, H =1, T1=[2,3,4], T2= .
So, on the next recursion,
X=2, list = [2,3,4], H matches X, thus line 1 will get executed.
Therefore, X=2, T=[3,4]
So, it should print = [3,4].(I know, [1,3,4] is the right answer. I am not able to understand the explanation behind this code)
My, question is, what is wrong in my understanding?
And, what is the use of [H|T2] in
mydelete(X,[H|T1],[H|T2]) :- mydelete(X,T1,T2).
Thanks! Please help me out!
edit: I tried removing H from [H|T2]. It is printing [3,4]. How H is adding 1 as the prefix to the list [3,4] ?
The best way to think of this as an imperative programmer is that the last argument is kind of the return value. You see that the first call you make "returns" [H|T2], not merely T2 this is how the first element of the list remains: after recursing to compute the value of T2, mydelete is adding H (which happens to equal 1 in this case) to the start of the returned list.
What is the use of [H|T2]?
In your explanations, you forgot to consider that the third argument, L, is being unified with [H|T2]. Up to this point, L was free (in your case), and now you know that it is a list starting with H. The rest of the list T2 is now the third argument to the recursive call and will be unified likewise, until you reach the base case.
By the way, what happens when your list is empty?
I am trying to write some Prolog code to take a list such as:
[[park, joe], [park, bob], [park, kate], [school, joe], [zoo, amy], [zoo, ted]].
and organizes the list into the form:
[[park,[joe, bob, kate]], [school,[joe]], [zoo,[amy, ted]]].
It can be assumed that all matching heads of each element (park = park, zoo = zoo) are directly next to each other in the list because the code I made to create the list sorts it in alphabetical order. I can't seem to figure out how to accomplish this and seem to get errors at every turn :(. Below is the code that I have so far in the last state that it ran without errors and I will try to explain what I was thinking.
merge([],[]).
merge([First|Rest], Z) :-
merge(Rest, U),
[Meet1, Person1] = First,
( =(U, []) -> % beginning case from recursion, U is empty
Meet2 = [],
Person2 = [];
[[Meet2|Person2]|_] = U),
( =(Meet1, Meet2) -> % Case of matching heads, combine the tails
print('Match '),
append([First], U, Z);
print('No-match '), % otherwise, not matching
append([First], U, Z) ).
So what I was trying to do is use appends to add all of the changes to U and return it to the console with Z. such as,
( =(Meet1, Meet2) ->
append(Person1, Person2, Combpersons),
append([Meet1], [Combpersons], T),
append(T, U, Z);
...no match code here..).
However my code keeps ending prematurely with a false when I try to change or add appends like this in the first block of code I put here. Even a change such as turning append([First], U, Z) into append([Meet1], U, Z) makes my code end with a false and I am not understanding why. Any help/hints on creating a solution would be appreciated.
I think that learning any language it's a process where low and high level issues must be interleaved. So far, you're learning the basic syntax. But why you use such unreadable constructs ? And of course, any programming language builds upon a set of patterns, usually covered by libraries. Consider
l2p([A,B],A-B).
?- maplist(l2p,[[park, joe], [park, bob], [park, kate], [school, joe], [zoo, amy], [zoo, ted]], L),group_pairs_by_key(L,G).
L = [park-joe, park-bob, park-kate, school-joe, zoo-amy, zoo-ted],
G = [park-[joe, bob, kate], school-[joe], zoo-[amy, ted]].
Anyway, here is your code restructured:
merge([],[]).
merge([[Meet, Person]|Rest], Z) :-
merge(Rest, U),
( U = []
-> % beginning case from recursion, U is empty
Z = [[Meet, [Person]]]
; U = [[Meet, Persons] | Rest1]
-> % Case of matching heads, combine the tails
Z = [[Meet, [Person | Persons]] | Rest1]
; % otherwise, not matching
Z = [[Meet, [Person]] | U]
).
If you had your initial list as a list of pairs instead, you could use library(pairs), available in SWI-Prolog.
?- group_pairs_by_key([park-joe, park-bob, park-kate, school-joe, zoo-amy, zoo-ted], G).
G = [park-[joe, bob, kate], school-[joe], zoo-[amy, ted]].
Using the library gives you more than just this "reorganization". There is also library(ugraphs), which might be better suited, depending on what you are doing.
Your code fails, because you are trying to extract the place and the person before checking if there are any.
No use for append here, the reordering looks pretty complicated, but I did my best with the variable names. Furthermore you'll only need one ->: If you hadn't found a tuple with the same first coordinate, you'll want a to start a new, it doesn't matter if there was none before.
merge([],[]).
merge([[Place,Person]|Rest], [[Place,Group]|OtherGroups]) :-
merge(Rest, U),
(U = [[Place,Others]|OtherGroups] ->
Group = [Person|Others];
[OtherGroups,Group] = [U, [Person]]).
Edit: I changed the solution for readablity reasons.