【问题标题】:How to Solve Cryptarithmetic Puzzle in Prolog如何在 Prolog 中解决密码学难题
【发布时间】:2012-12-14 00:35:08
【问题描述】:

我必须编写一个 Prolog 程序来解决密码难题。

我需要编写一个函数solve([A, M, P, D, Y]) 将变量[A, M, P, D, Y] 分配给0到9的值,以便满足方程上午 + 下午 = 天。每个变量赋值不同,A、P、D不能等于0。

我开始编写这个函数,但是在运行我的程序时遇到了问题。我将 A、P 和 D 的限制设置为不为零。当我浏览算法时,我意识到 D 必须为 1,所以我在程序的开头定义了它。我为 M 定义了两个不同的变量(M1 和 M2)并将它们设置为彼此相等,因为拼图中的不同 M 应该分配给相同的值。我将位置分配给不同的变量,并根据谜题将它们相加。我解释了任何变量携带进位变量。我的程序编译了,但是函数没有执行。

solve([A, M1, M2, P, D, Y]):- D is 1,
A/=0,
P/=0,
D/=0,
M1 = M2,
select(M1, [0,2,3,4,5,6,7,8,9], R1),
select(M2, R1, R2),
Y is (M1+M2) mod 10,
C1 is (M1+M2) // 10,
select(Y, R2, R3),
select(A, R3, R4),
select(P, R4, R5),
select(D, R5, R6),
A is (A+P+C1) mod 10,
D is (A+P+C1)// 10.

我做错了什么?我的变量定义有问题吗?我需要定义两个不同的 M 变量,还是一个就足够了?

【问题讨论】:

  • 你为什么在开始时检查 A,P,D ?您需要为 A、P、D 赋值,然后检查它们是否不同于零。

标签: prolog clpfd cryptarithmetic-puzzle


【解决方案1】:

这是我为您解决难题的解决方案。我们只是依靠 PROLOG 的回溯。我们首先选择所有变量,然后检查拼图条件。我不认为你需要定义两个女士。

solve([A,M,P,D,Y]):- 
select(A,[0,1,2,3,4,5,6,7,8,9],WA), % W means Without
not(A=0),
select(M,WA,WMA),
select(P,WMA,WMAP),
not(P=0),
select(D,WMAP,WMAPD),
not(D=0),
select(Y,WMAPD,WMAPDY),
DAY is 100*D+10*A+Y,
AM  is 10*A+M,
PM  is 10*P+M,
DAY is AM+PM.

【讨论】:

  • 感谢您的回复。对此,我真的非常感激。另外,有没有办法使用 mod 10 方式来解决它(正如我在原始帖子中尝试实现的那样)?
  • 我想你可以用 mod 10 来做,但是为了找到 Y 例如(使用 mod 10),我们需要先得到 DAY,然后 Y 就是 DAY mod 10,A 是 ( DAY div 10) mod 10,D 是 DAY div 100,上午和下午也是如此。但是 DAY 是一个三位数,因此选择将来自 [100,101,...,99+99] for DAY,[10,11,...,99] for AM,PM (2 digits ) 。希望这会有所帮助。
  • 另外,你为什么要添加一个 WA(没有 A)变量? As和Ms有重复。
  • Ehem 与 select(A,[0,1....,9],WA) 我们得到一些列表,其中不包含 A 的值(我将其命名为 WA )。还有 A in DAY 和 in AM 必须具有相同的值。
【解决方案2】:

你写:“我的程序编译但函数没有执行:”

solve([A, M1, M2, P, D, Y]):- D is 1,
    A/=0,

难怪。首先,Prolog 中没有 /= 运算符。我假设您的意思是\=。但是A \= B的意思是“A不能和B统一”。在您的情况下,B 是 0,但 A 是尚未设置的逻辑变量。它可以与anything统一。你应该只使用\=检查不等式,所有涉及的日志变量都被实例化之后!

所以,A \= 0 失败。 (还有一点,M1=M2 是多余的,你可以一直使用M)。

解决此类难题的通用工具是unique selection from narrowing domains

selectM([A|As],S,Z):- select(A,S,S1),selectM(As,S1,Z).
selectM([],Z,Z).

有了它,你的谜题就简单了

solve([A,M,P,D,Y]):-
  selectM([A,P,D],[1,2,3,4,5,6,7,8,9],R),     % R is the remaining domain
  selectM([M,Y],[0|R],_),                     % don't care what remains
  10*(A+P)+M+M =:= 100*D+10*A+Y.

您有一个正确的想法,即尽可能在搜索之前找出分配。使用你的方法,它可以写成

solve([A,M,P,D,Y]):-    
  selectM([M,A],[0,1,2,3,4,5,6,7,8,9],R),
  A =\= 0,
  Y  is (M+M) mod 10,     % AM+PM=DAY
  C1 is (M+M) // 10,
  A  is (A+P+C1) mod 10,
  D  is (A+P+C1) // 10,
  selectM([P,D,Y],R,_),   % ensure all are different
  p =\= 0, D =\= 0.

同样,我们必须在测试其值之前选择A

【讨论】:

    【解决方案3】:

    除了其他人发布的内容之外,我想提出一个稍微不同的看法。

    以下解决方案使用 GNU Prolog 及其 CLP(FD) 约束。此类约束在所有广泛使用的 Prolog 系统中都可用。

    解决方案(Vs): - Vs = [A,M,P,D,Y], fd_domain(Vs, 0, 9), A*10 + M + P*10 + M #= D*100 + A*10 + Y, fd_all_different(Vs), 一个#\= 0, P #\= 0, D #\= 0。

    我现在强调在这种情况下使用 CLP(FD) 约束的几个关键优势

    首先,很明显,我们可以在具有此类约束的情况下以非常直接的方式对所有需求进行建模。该程序实际上是您的任务到内置关系的几乎逐字翻译:

    • 我需要写一个函数solve([A, M, P, D, Y])

      我使用solution/1 而不是命令式solve/1,因为谓词所有方向都有意义,包括所有变量都已经的特定实例绑定到具体的整数。在这种情况下,我们可以使用谓词来验证解决方案。在难题已经完全解决的情况下,称其为“解决”是没有意义的。此外,我们可以使用谓词完成部分实例化的解决方案。在 Prolog 中,避免谓词名称的命令是很好的做法。

    • 将变量 [A, M, P, D, Y] 分配给从 0 到 9 的值

      这是通过fd_domain(Vs, 0, 9) 声明的。

    • 所以它满足等式 AM+PM=DAY。

      因此等式是A*10 + M + P*10 + M #= D*100 + A*10 + Y

    • 每个变量都被赋予不同的值

      这由内置约束fd_all_different/1 表示。

    • 而A、P、D不能等于0。

      这是通过A #\= 0 等声明的。

    其次,我们可以使用最通用的查询来研究约束传播的效果:

    | ?- 解决方案([)。 Vs = [_#3(2..8),_#24(5..8),9,1,_#87(0:2..6)]

    或者,换个说法:

    | ?- 解决方案([A,M,P,D,Y])。 A = _#3(2..8) D = 1 M = _#24(5..8) P = 9 Y = _#87(0:2..6)

    这证实了你所说的:D 在这个谜题中是必然 1。这也显示了一些超出您发现的其他有趣的东西:P 必然 9。此外,M 以及A 和@987654332 的域具有相当严格的界限@ 也被大幅删减。

    这表明约束传播已显着减少搜索空间。

    具体的解决方案是什么样的?以下是几个例子:

    | ?- 解决方案(Vs),fd_labeling(Vs)。 Vs = [2,5,9,1,0] ? ; Vs = [2,7,9,1,4] ? ; Vs = [2,8,9,1,6] ? 是的

    第三,您可以运行不同的标签选项来尝试各种搜索策略来探索解决方案空间,无需更改或重新编译程序。

    最后,显着减少的搜索空间通常会产生更快的程序。我把它作为一个练习来运行一些基准测试,以显示在这种情况下基于 CLP(FD) 的版本的速度有多快。

    请参阅 ,了解有关这一重要声明范式的更多信息。

    【讨论】:

      【解决方案4】:

      我认为您的问题是对 D 的多个“分配”。首先 D 绑定到 1,之后无法更改值(Prolog 使用统一,而不是分配)。然后两个

      ...
      select(D, R5, R6),
      ...
      D is (A+P+C1)// 10.
      

      当 D 不等于 1 时会失败

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-06-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多