2

7 个不同的字母中的每一个都代表一个不同的数字。目的是找到字母的数字替换,以使结果总和在算术上是正确的。然后,该解决方案应产生满足上述加法问题的所有数字组合。输入一个查询,例如crypto(P,I,N,G,O,F,U)应该返回您的解决方案。

密码算术难题是这样的:

  P I N G
  P O N G
+   F U N
---------
I G N I P
4

2 回答 2

2

使用根据我之前对一个非常相似的问题的回答,我们运行以下查询:

?- 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.
于 2015-08-13T09:31:09.297 回答
0

假设这是我们正在谈论的简单替换密码(只是为了好玩),我会尝试一下。应该注意的是,这是完全未经测试的。

我将以通用方式进行设置,因此您可以这样说:

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

我们将制定规则,加密的东西由原子表示,所以你可以这样说:

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

并获得您期望的结果。

所以...

首先,您需要在密文中找到一组(离散的、唯一的)字符:

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

上面遍历表达式的解析树a * b + c * d,找到每个叶节点(原子),将它们解构为组成它们的字符。setof/3确保结果列表已排序且唯一。

一旦你有了它,你就需要一种方法来生成所有可能的键(键 == 字符和数字之间的映射)。我们希望能够说类似的话

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

然后回来

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

等等

所以:

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

然后你需要一种方法来解码一个密码表达式,比如

ping + pong + fun

并[尝试]将其转回正确的数字。这与遍历解析树并枚举叶节点原子没有太大区别,但在这里我们需要将它们恢复为数字形式。

如果表达式是一个原子,我们

  • 把它分解成它的组成字符,
  • 使用我们的键,将每个字符映射到其对应的数字,
  • 然后我们把那个数字列表变回一个数字

    解码(密钥,CipherExpr,PlainExpr):- atom(CipherExpression),atom_chars(CipherExpression,Cs),findall(D,(成员(C,Cs),成员(C:D,Key)->真;D = C) , Ds ) , number_chars( PlainExpr , Ds ) 。

一般情况很容易。像这样的中缀表达式ping + pong实际上是序言词+(ping,pong)。我们:

  • 将中缀项分解ping + pong为运算符 ( +) 及其左右子表达式。
  • 然后我们递归解码左右子表达式
  • 最后,我们重新组装 [decoded] 表达式。

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

然后你可以把它们放在一起:

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
  .
于 2015-05-19T01:16:39.527 回答