我必须编写一个Prolog程序来解决密码算术难题。
我需要编写一个函数solve([A,M,P,D,Y]),它将变量[A,M,P,D,Y]分配给0到9之间的值,使其满足等式AM + PM = DAY。每个变量都分配给一个不同的值,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变量,还是一个足够?
答案 0 :(得分:3)
这是我的解谜方案。我们只依靠PROLOG的回溯。我们首先选择所有变量,然后检查拼图条件。我认为你不需要定义两个Ms。
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.
答案 1 :(得分:1)
你写道:“我的程序编译但函数没有执行:”
solve([A, M1, M2, P, D, Y]):- D is 1,
A/=0,
难怪。首先,Prolog中没有/=
运算符。我假设你的意思是\=
。但A \= B
表示“A不能与B统一”。在您的情况下,B
为0,但A
是尚未设置的逻辑变量。它可以与任何统一。您应该只使用\=
来检查不等式,在所有涉及的日志变量实例化之后!
因此,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
。
答案 2 :(得分:1)
除了其他人发布的内容之外,我想对此略有不同。
以下解决方案使用GNU Prolog及其 CLP(FD)约束。所有广泛使用的Prolog系统都可以使用这些约束。
solution(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), A #\= 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
等声明的
其次,我们可以使用最通用的查询来研究约束传播的影响:
| ?- solution([). Vs = [_#3(2..8),_#24(5..8),9,1,_#87(0:2..6)]
或者换句话说:
| ?- solution([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
的域非常严格1}}和Y
也得到了明显的修剪。
这表明约束传播显着减少搜索空间。
具体解决方案是什么样的?以下是一些例子:
| ?- solution(Vs), fd_labeling(Vs). Vs = [2,5,9,1,0] ? ; Vs = [2,7,9,1,4] ? ; Vs = [2,8,9,1,6] ? yes
第三,您可以运行不同的标签选项来尝试各种搜索策略来探索解决方案空间,而无需更改或重新编译程序。
最后,显着缩小的搜索空间通常会产生快得多的程序。我把它作为练习来运行一些基准测试,显示基于CLP(FD)的版本在这种情况下的速度有多快。
有关此重要声明范例的详细信息,请参阅 clpfd 。
答案 3 :(得分:0)
我认为你的问题是D的多个'赋值'。首先D绑定到1,之后不能改变值(Prolog使用统一,而不是赋值)。然后两个
...
select(D, R5, R6),
...
D is (A+P+C1)// 10.
当D不同于1时,将失败