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
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
使用clpfd!根据我之前对一个非常相似的问题的回答,我们运行以下查询:
?- 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.
假设这是我们正在谈论的简单替换密码(只是为了好玩),我会尝试一下。应该注意的是,这是完全未经测试的。
我将以通用方式进行设置,因此您可以这样说:
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
.