Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Arithmetic puzzle

48 views
Skip to first unread message

Rap...@gmail.com

unread,
Nov 30, 2006, 9:22:40 AM11/30/06
to
Hello guys, I think a lot of you know arithmetic puzzle, where you need
to insert arithmetic signs (operators) between numbers to get right
equations. Now I have similar task, I need to put plus or minus sign
between different digits to get given answer, lets say I have such
query:
?- find([1,2,3,4,7], 30).
30 = 1 + 2 + 34 - 7

Yes, if you don't put any sign between two digits, you get two digit
number. It can be three digit, even whole given list can be one number,
for example:
?- find([1,2,3,4], 1234).
1234 = 1234.

So, I'm thinking about multiway tree, root is first digit, first node
from left is second digit, and between root and its child there is
nothing so it makes two digit number, middle node has second digit, and
between root and this child, there is plus sign, and another node for
the minus sign. And a problem I have, that I don't know ho to implement
that... Can anybody help me with this? Maybe there is a better solution
than multiway tree. Thanks in advance;)

Jussi Piitulainen

unread,
Nov 30, 2006, 9:47:08 AM11/30/06
to
Rap...@gmail.com writes:

> Hello guys, I think a lot of you know arithmetic puzzle, where you need
> to insert arithmetic signs (operators) between numbers to get right
> equations. Now I have similar task, I need to put plus or minus sign
> between different digits to get given answer, lets say I have such
> query:
> ?- find([1,2,3,4,7], 30).
> 30 = 1 + 2 + 34 - 7
>
> Yes, if you don't put any sign between two digits, you get two digit
> number. It can be three digit, even whole given list can be one number,
> for example:
> ?- find([1,2,3,4], 1234).
> 1234 = 1234.
>
> So, I'm thinking about multiway tree, root is first digit, first

You know how arithmetic expressions in Prolog are tree-structured
terms already, do you? Those (-)/2 and (+)/2 in there are functors.

You could just declare a new operator for concatenation, or maybe use
an existing non-arithmetic operator for that, like (.)/2, so that your
example would be ((1 + 2) + [3|4]) - 7, say. That is a binary tree
with your numbers as leaves. (As usual, [3|4] = .(3,4)).

I don't know if there is a way to extend is/2, but it is an easy
exercise to implement a similar evaluator with standard rules for a
handful of standard operators and an extra rule to evaluate [3|4] as
10*3 + 4.

russell kym horsell

unread,
Nov 30, 2006, 10:05:39 PM11/30/06
to
Rap...@gmail.com wrote:
> Hello guys, I think a lot of you know arithmetic puzzle, where you need
> to insert arithmetic signs (operators) between numbers to get right
> equations. Now I have similar task, I need to put plus or minus sign
> between different digits to get given answer, lets say I have such
> query:
> ?- find([1,2,3,4,7], 30).
> 30 = 1 + 2 + 34 - 7
[...]

I think the simplest stategy is to create a plan (with an
accumulation param) as a "find" helper predicate recurses.

I.e. to find plan to convert X,Y|Restofdigits into Result (number)
you can try to append X to next digit Y, put + sign between X and next digit Y,
or put - sighn between X and next digit Y. So "find" will be a predicate
with at leat 3 choices. When it finally runs out of digits it will
have the plan in its accumulation param. The plan can be as simple as
a Prolog expression -- which can then be evaluated (e.g. wih "is")
numerically compared against Result.

This solution may be vague due to eggnog overload. Sorrick.

arv832

unread,
Dec 2, 2006, 6:13:01 AM12/2/06
to
:- import const_domain.

find(List,Res) :-
signs(List,Sum,SignVars),
SignVars in [-1,1],
Res ?= Sum.

signs([],0,[]).
signs([X|XT],(S * X + SumTail),[S|ST]) :-
signs(XT,SumTail,ST).

arv832

unread,
Dec 2, 2006, 6:14:58 AM12/2/06
to
Of course this does not combine the numbers 3,4 into 34

ronaldo

unread,
Dec 3, 2006, 8:07:06 AM12/3/06
to
Hello guys,

I am quite novice in prolog and I have been trying to do it by
myself while following this thread. This is the solution I have
elaborated:

------------------------------------------------------------------------------------
% Visual representations of operators
operator(sum,' + ').
operator(rest,' - ').
operator(con,'').

% Definition of every operator used
sum(N1,N2,R) :- R is N1+N2.
rest(N1,N2,R):- R is N1-N2.
con(N1,N2,R) :- R is N1*10+N2.

% Predicates for inserting operators between numbers
insert_op(L, N, [N|[sum|L]]).
insert_op(L, N, [N|[rest|L]]).
insert_op(L, N, [N|[con|L]]).

% This is used for printing the resulting list
writeOp([]).
writeOp([O|LT]):-operator(O,OP), !, write(OP), writeOp(LT).
writeOp([L|LT]):-write(L), writeOp(LT).

% Rules for calculating the arithmetic result of the generated
operation
calculate([N1|[OP|[N2|LT]]], R) :-
!, call(OP,N1,N2,RES),
calculate([RES|LT], R).
calculate([R], R).

% Rules for generating every possible operation with numbers
generate([],[]).
generate([L|LT],LOP):-
generate(LT,O),
( O \= []
-> insert_op(O, L, LOP)
; LOP = [L]
).

% Find rule which generates operations until it finds a correct one
find(L,R) :-
generate(L,OP),
calculate(OP, R),
write(R), write(' = '), writeOp(OP).
------------------------------------------------------------------------------------

Could you please give me constructive comments?
Many Thanks in advance :)

bart demoen

unread,
Dec 3, 2006, 9:07:52 AM12/3/06
to
On Sun, 03 Dec 2006 05:07:06 -0800, ronaldo wrote:

> Hello guys,
>
> I am quite novice in prolog and I have been trying to do it by
> myself while following this thread. This is the solution I have
> elaborated:

...


>
> Could you please give me constructive comments?
> Many Thanks in advance :)

Maybe we can first agree on what to expect - the query

?- find([1,2,3],4).

fails for your program, while my program prints as answers
4 = 2 - 1 + 3
Yes ;
4 = 2 + 3 - 1
Yes ;
4 = 3 - 1 + 2
Yes ;
4 = 3 + 2 - 1
Yes ;
No more !

Your generate uses the numbers only in the order of the inputlist.
Did you intend the failure ?


Cheers

Bart Demoen

Jussi Piitulainen

unread,
Dec 6, 2006, 10:59:41 AM12/6/06
to
ronaldo writes:

> operator(sum,' + ').
...


> sum(N1,N2,R) :- R is N1+N2.

...


> insert_op(L, N, [N|[sum|L]]).

...

I think it's better to build arithmetic expressions than lists,
possibly modified to have lists of digits as an expression type.

For one thing, you would learn something about Prolog terms and
operators. For another, arithmetic expressions have a nice written
representation already.

> calculate([N1|[OP|[N2|LT]]], R) :-
> !, call(OP,N1,N2,RES),
> calculate([RES|LT], R).
> calculate([R], R).

It's better to write [N1,OP,N2|LT] than [N1|[OP|[N2|LT]]].

The power of call/N is not needed here. You could write a single
predicate to evaluate all your operators:

eval(sum, N1, N2, R) :- R is N1 + N2.
...

However, if you used (3 + (14 + 1)) in place of [3,sum,14,sum,1],
you could just hand it to is/2 for evaluation: R is E.

And if you used something like (3 + (con(1,4) + 1)), you could
easily write your own version of is/2 so:

calculate(N, N) :-
number(N).
calculate(L + R, V) :-
calculate(L, M),
calculate(R, N),
V is M + N.
calculate(con(E,G), V) :-
% we should be careful about con(3,con(1,4)), though:
% don't want 3*10 + 1*10 + 4 but 3*100 + 1*10 + 4;
% oops; one way is to allow only con(con(3,1),4)
% when building the expression.
...

This kind of recursion follows the structure of the data, which is
usually nice. Try some expressions:

(L + R) = (3 + 1 + 4 - 1 + 5 + 9)
(L + R) = (one + two)

Incidentally, I wrote my own solution, and it turned out rather
nice when I simply either treated a digit sequence as a possible
expression (with a special rule to evaluate it but not using any
special concatenation operator after all) or split it in two,
treating the parts recursively to get L and R and making L + R and
L - R out of those.

ronaldo

unread,
Dec 9, 2006, 3:56:54 PM12/9/06
to
Really interesting comments, guys. I am realizing that my knowledge
about prolog is even less than I thought :).

Many thanks for your interest. I will think about your propositions to
start learning to think in prolog.

Bart: It was intended. I thought the problem was just to introduce
signs between the numbers, without changing their order. Of course, if
you find solutions with numbers in a different order, you are solving a
more general case. Really interesting :).

Peter Van Weert

unread,
Dec 11, 2006, 4:19:26 PM12/11/06
to
bart demoen schreef:

I wrote a small program myself (cf attachment). I have been playing with
a bit, so the version attached is a tiny bit more general then the
original problem, and will find somewhat more solutions. But it is easy
enough to play with different settings: you can disallow the extra
operators, disallow permutations, disallow the adding of sign operators,
etc. by some simple changes to the program.

Cheers,
Peter

puzzle.pl

bart demoen

unread,
Dec 11, 2006, 4:46:36 PM12/11/06
to
On Mon, 11 Dec 2006 22:19:26 +0100, Peter Van Weert wrote:


> find(Nums, Outcome, Expression) :-
> permutation(Nums, Perm),
> generate(Perm, Expression),
> Outcome is Expression.
>
> generate(Ns, Expr) :- generate_(Ns, Expr).
> generate(Ns, -Expr) :- generate_(Ns, Expr).
>
> generate_([N], N).
> generate_([N|Ns], Expr) :-
> operator(Op),
> generate(Ns, ExprX),
> ( Expr =.. [Op,N,ExprX] ; Expr =.. [Op,ExprX,N] ).
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Since you already permute the input, this disjunction is just generating
duplicate answers, right ?

Cheers

Bart Demoen

Richard Szopa

unread,
Dec 11, 2006, 7:25:32 PM12/11/06
to

On Dec 11, 10:19 pm, Peter Van Weert <Peter.VanWe...@cs.kuleuven.be>
wrote:

> find(Nums, Outcome, Expression) :-
> permutation(Nums, Perm),

[...]
> operator(/).

In some situations your program generates a division by zero error:

?- find([1,2,3,5], 1235, E).
ERROR: //2: Arithmetic: evaluation error: `zero_divisor'

I would probably suffice if your predicate caught it and then failed.


Anyway, I couldn't resist the temptation of writing my own program
solving the puzzle ;-)

expression --> number.
expression --> number, op, expression.

op --> [+].
op --> [-].
op --> [*].
op --> [/].
op --> ['']. % concatenation

number --> [1].
number --> [2].
number --> [3].
number --> [4].
number --> [5].
number --> [6].
number --> [7].
number --> [8].
number --> [9].

% put gaps between the numbers for the operators.
prepare_expression([X, Y], [X, _, Y]).
prepare_expression([H|T],[H, _|Rest]) :-
prepare_expression(T, Rest).

list_to_term(L, T) :- % is there anything built-in that does this?
concat_atom(L, A),
term_to_atom(T, A).

arithmetic_puzzle(Numbers, Result, ToEval) :-
permutation(Numbers, NumbersPerm),
prepare_expression(NumbersPerm, Expression),
expression(Expression, []),
list_to_term([Result, =:=|Expression], ToEval),
call(ToEval).

Any comments?

Cheers,

-- Richard

Peter Van Weert

unread,
Dec 12, 2006, 3:26:52 AM12/12/06
to bart demoen
bart demoen schreef:

Not quite. If we do not have this disjunction, the first operand can
never be an expression. So answers like:

?- find([1,2,3], 2, X).
...
X = (1+3)/2 ;

X = (3+1)/2 ;
...

are excluded. Of course, this is only interesting if you have
non-commutative operators, like /.

Cheers,
Peter

Peter Van Weert

unread,
Dec 12, 2006, 3:38:42 AM12/12/06
to Richard Szopa
Richard Szopa schreef:

In a way you do not add parentheses, so you do not get answers like:

?- find([1,2,3], 2, X).
...
X = (1+3)/2 ;

X = (3+1)/2 ;
...

This is also how you 'avoid' the division by zero at first glimpse (also
by not including zero as a number of course). You will never get things
like x / (y - y).

Cheers,
Peter

Richard Szopa

unread,
Dec 15, 2006, 4:54:29 PM12/15/06
to
I decided to write a solution to Peter's extension of the arithmetic
puzzle (i.e. allowing bracket constructions). While trying to do it, I
managed to make better (or at least shorter) the old predicate :-).

As always, comments are welcome.

% the old problem, better

expression([X])--> [X].
expression([H|T]) --> [H], op, expression(T).

op --> [+].
op --> [-].
op --> [*].
op --> [/].
op --> ['']. % concatenation

arithmetic_puzzle(Numbers, Result, ToEval) :-
permutation(Numbers, NumbersPerm),
expression(NumbersPerm, Expression, []),
concat_atom([Result, =:=|Expression], At),
term_to_atom(ToEval, At),
call(ToEval).

% Peters extension: we allow bracket constructions

number([X]) --> [X].
number([H|T]) --> [H], number(T).

expr_brackets(X) --> number(X).
expr_brackets([H|T])--> {append(Left, Right, T),
Right\=[]}, ['('],
expr_brackets([H|Left]), [')'], opbr, ['('],
expr_brackets(Right), [')'].


opbr --> [+].
opbr --> [-].
opbr --> [*].
opbr --> [/].

arithmetic_puzzle_2(Numbers, Result, ToEval) :-
permutation(Numbers, NumbersPerm),
expr_brackets(NumbersPerm, Expression, []),
concat_atom([Result, '=:= '|Expression], At),
term_to_atom(ToEval, At),
catch(ToEval, error(evaluation_error(zero_divisor),_), fail). %
we catch division by zero

The only thing I'm not sure is whether my grammar for making
bracket-expressions is the best possible. Could this be done without
using append/3?

Cheers,

-- Richard

Markus Triska

unread,
Dec 16, 2006, 9:45:29 AM12/16/06
to
"Richard Szopa" <ryszar...@gmail.com> writes:

> % Peters extension: we allow bracket constructions
>
> number([X]) --> [X].
> number([H|T]) --> [H], number(T).
>
> expr_brackets(X) --> number(X).
> expr_brackets([H|T])--> {append(Left, Right, T),
> Right\=[]}, ['('],
> expr_brackets([H|Left]), [')'], opbr, ['('],
> expr_brackets(Right), [')'].


Given the way expr_brackets/3 is used in your program, you can write:

expr_brackets(Es) --> Es.
expr_brackets(Es) -->
{ append([L|Ls], [R|Rs], Es) },
['('], expr_brackets([L|Ls]), [')'],
opbr,
['('], expr_brackets([R|Rs]), [')'].


All the best,
Markus.

Richard Szopa

unread,
Dec 16, 2006, 10:22:26 PM12/16/06
to
Markus Triska <tri...@gmx.at> writes:

> expr_brackets(Es) --> Es.
> expr_brackets(Es) -->
> { append([L|Ls], [R|Rs], Es) },
> ['('], expr_brackets([L|Ls]), [')'],
> opbr,
> ['('], expr_brackets([R|Rs]), [')'].

Thank you! I wouldn't have thought about it. Your article made some
gears in my head move and now I think I understand DCG in Prolog a bit
better.

-- Richard

0 new messages