【问题标题】:How can I do cryptarithmetics in Prolog?我如何在 Prolog 中进行密码计算?
【发布时间】:2015-05-18 09:56:19
【问题描述】:

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

【问题讨论】:

  • hmm.... 您是否尝试在 Google 上搜索“Prolog 发送更多资金”?根据 stackoverflow 指南,您的问题不符合真正的问题
  • 我没有看到任何问题。语句末尾有一个问号,构成主语,但不构成问题。您尝试了哪些方法,在解决问题的过程中遇到了哪些障碍?
  • @false:链接显然只有在登录时才能正确解析!

标签: prolog cryptarithmetic-puzzle


【解决方案1】:

使用!基于my previous answervery similar question,我们运行以下查询:

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

【讨论】:

    【解决方案2】:

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

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

    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_chars(CipherExpression,Cs) , findall( D , ( 成员 (C,Cs), 成员(C:D,Key) -> true ; D=C ) , Ds ) , number_chars(PlainExpr, Ds) .

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

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

      解码(密钥,CipherExpr,PlainExpr):- CipherExpr =.. [Op,L,R] , 解码(L,L1), 解码(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
      .
    

    【讨论】:

      猜你喜欢
      • 2017-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-13
      • 1970-01-01
      相关资源
      最近更新 更多