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
そして、それを適切な数に戻してください。これは、解析ツリーをたどってリーフ ノード アトムを列挙することと大差ありませんが、ここではそれらを数値形式に戻す必要があります。
式がアトムの場合、
次に、その数字のリストを数値に戻します
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 ) .
一般的なケースは簡単です。のような中置式ping + pong
は、実際にはプロローグ用語+(ping,pong)
です。私達:
ping + pong
を演算子 ( +
) とその左右の部分式に分解します。最後に、[デコードされた] 式を再構築します。
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
.