2

Each of the 7 different letters stands for a different digit. The aim is to find a substitution of digits for the letters such that the resulting sum is arithmetically correct. The solution should then produce all of the combinations of the digits that satisfy the addition problem above. Putting in a query such as crypto(P,I,N,G,O,F,U) should return your solution.

The cryptarithmetic puzzle goes like this:

  P I N G
  P O N G
+   F U N
---------
I G N I P
repeat
  • 18,496
  • 4
  • 54
  • 166
konopoly
  • 155
  • 1
  • 1
  • 10
  • 2
    hmm.... did you try to google for "Prolog send more money"? And according to stackoverflow guidelines, your question does not qualify to be a real question –  May 18 '15 at 10:06
  • 1
    I don't see a question. There's a question mark at the end of the statement which forms the subject, but it doesn't form a question. What have you tried, and where are you blocked in your efforts to solve the problem? – lurker May 18 '15 at 12:49
  • 1
    See [these questions](http://stackoverflow.com/questions/tagged/prolog+cryptarithmetic-puzzle). – false May 18 '15 at 19:13
  • @false: the link is apparently only resolved correctly if one is logged in! – mat May 19 '15 at 08:46

2 Answers2

2

Use ! Based on my previous answer to a very similar question, we run the following query:

?- Eq = ([P,I,N,G] + [P,O,N,G] + [F,U,N] #= [I,G,N,I,P]),
   crypt_arith_(Eq,Zs),
   labeling([],Zs).
  Eq = ([7,1,9,4] + [7,0,9,4] + [6,2,9] #= [1,4,9,1,7]), Zs = [7,1,9,4,0,6,2]
; Eq = ([8,1,4,7] + [8,3,4,7] + [9,2,4] #= [1,7,4,1,8]), Zs = [8,1,4,7,3,9,2]
; Eq = ([8,1,4,7] + [8,9,4,7] + [3,2,4] #= [1,7,4,1,8]), Zs = [8,1,4,7,9,3,2]
; false.
Community
  • 1
  • 1
repeat
  • 18,496
  • 4
  • 54
  • 166
0

Assuming this is a simple substitution cipher we're talking about (and just for fun), I'll take a stab at it. One should note that this is completely untested.

I'm going to set this up in a generic way, so you can say something like:

substitution_cipher( CipherExpr , CipherResult , Expr , Result , Key ).

We'll make the rule that the enciphered stuff is represented by atoms, so you can say something like this:

substitution_cipher( ping + pong + fun , ignip , Expr , Sum , Key ) .

And get the results you'd expect.

So...

First, you need the set (discrete, unique) of characters found in the cipher text:

character_set( Expr , Charset ) :-
  setof( C , A^Cs^( atoms_in_expression( Expr , A ) , atom_chars(A,Cs) , member(C,Cs) ) , Charset ) .

atom_in_expression( Expr , Value ) :- atom(Expr) .
atom_in_expression( Expr , Value ) :-
  Expr =.. [ _ , Left , Right ] ,
  (
    values( Left  , Value )
  ;
    values( Right, Value
  ) .

The above walks the parse tree of an expression like a * b + c * d, finding each of the leaf nodes (atoms), deconstructing them into the characters that comprise them. setof/3 ensures that the resulting list is sorted and unique.

Once you have that, you need a way of generating all the possible keys (key == mapping between a character and a digit). We want to be able to say something like

generate_key( [a,b,c] , Key )

and get back

Key = [a:1,b:2,c:3]

etc.

So:

generate_key( Charset , Key ) :-
  generate_key( Charset , [] , Key ) .

generate_key( [] , Key , Key ) .      % when we've exhausted the character set, we're done.
generate_key( [C|Cs] , Acc , Key ) :-  % otherwise...for each character
  digit(D) ,                           % find a digit
  \+ member( _:D , Acc ) ,             % that hasn't yet been used
  generate_key( Cs , [C:D|Acc] , Key ) % map it to the character and recurse down.
  .

digit(D) :- between(0,9,X) , atom_number(D,X).

Then you need a way to decode a cipher expression like

ping + pong + fun

and [try to] turn it back into proper numbers. This isn't much different than walking the parse tree and enumerating the leaf node atoms, but here we need to get them back into numeric form.

If the expression is an atom, we

  • decompose it into its constituent characters,
  • using our key, map each character to its corresponding digit,
  • then we turn that list of digits back into a number

    decode( Key , CipherExpr , PlainExpr ) :- atom(CipherExpression) , atom_chars(CipherExpression,Cs) , findall( D , ( member(C,Cs), member(C:D,Key) -> true ; D=C ) , Ds ) , number_chars( PlainExpr , Ds ) .

The general case is easy. An infix expression like ping + pong is really the prolog term +(ping,pong). We:

  • Decompose a infix term like ping + pong into an operator (+) and its left and right sub-expressions.
  • Then we recursively decode the left and right sub-expressions
  • Finally, we reassemble the [decoded] expression.

    decode( Key , CipherExpr , PlainExpr ) :- CipherExpr =.. [Op,L,R] , decode(L,L1) , decode(R,R1) , PlainExpr =.. [Op,L1,R1] .

Then you can put it all together:

substitition_cipher( CipherExpr , CipherResult , PlainExpr , PlainResult , Key ) :-
  character_set( CipherExpr = CipherResult , Charset ) ,
  generate_key( Charset, Key ) ,
  decode( Key , CipherExpr   , PlainExpr   ) ,
  decode( Key , CipherResult , PlainResult ) ,
  PlainResult =:= PlainExpr
  .
Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135