我有这个代码使用一个上限变量N,它应该终止于毕达哥拉斯三元组的X和Y.然而它只有在达到上限时才会冻结。不确定如何使用切割来阻止回溯。代码是:
is_int(0).
is_int(X) :- is_int(Y), X is Y+1.
minus(S,S,0).
minus(S,D1,D2) :- S>0, S1 is S-1, minus(S1,D1,D3), D2 is D3+1.
pythag(X,Y,Z,N) :- int_triple(X,Y,Z,N), Z*Z =:= X*X + Y*Y.
int_triple(X,Y,Z,N) :- is_int(S), minus(S,X,S1), X>0, X<N,
minus(S1,Y,Z), Y>0, Y<N.
将被调用,例如,
?- pythag(X,Y,Z,20).
答案 0 :(得分:7)
首先,让我们测试您的解决方案:
?- pythag(X,Y,Z,20). X = 4, Y = 3, Z = 5 ; X = 3, Y = 4, Z = 5 ; X = 8, Y = 6, Z = 10 ; X = 6, Y = 8, Z = 10 ; X = 12, Y = 5, Z = 13 ; X = 5, Y = 12, Z = 13 ; X = 12, Y = 9, Z = 15 ; X = 9, Y = 12, Z = 15 ; X = 15, Y = 8, Z = 17 ; X = 8, Y = 15, Z = 17 ; X = 16, Y = 12, Z = 20 ; X = 12, Y = 16, Z = 20 ...
对我来说很完美!所有答案都是正确的答案! ......包括最后一个解决方案。之后,你的程序循环。
在我们尝试找出问题之前,请坚持一下:你必须非常耐心地通过12(即12个)答案才能找到那个循环。你认为这种方法对更大的案例也适用吗?在你放弃之前,你愿意看多少答案?有没有更简单的方法来找出问题?
这里有一个有趣的观察结果:找到的答案(几乎)与循环程序无关!那就是:通过查看答案,你得到(经常 - 就像在这种情况下)没有关于循环的实际原因的线索!那么为什么不关闭所有答案并专注于相关部分!事实上,我们可以这样做:
?- pythag(X,Y,Z,20), false. ** LOOP **
现在,由于目标false
,所有答案都已被删除。剩下的只是最终结果:终止,或非终止,或一些错误。没有其他的。这应该有助于我们对终止一点点的观察 - 没有更多的盲目回答在屏幕上滚动。请注意,这并不能解决一般问题。毕竟, long 我们愿意等待多久? 1s? 1米?
通过查看相关的故障切片,可以最好地理解不终止的实际原因。这是程序的一个片段,其非终止意味着整个程序没有终止。 See this answer for more details。以下是查询pythag(X,Y,Z,20), false
的程序的相关失败切片:
pythag(X,Y,Z,N) :- int_triple(X,Y,Z,N), false,Z*Z =:= X*X + Y*Y. int_triple(X,Y,Z,N) :- is_int(S), false,minus(S,X,S1), X>0, X<N,minus(S1,Y,Z), Y>0, Y<N.is_int(0) :- false.is_int(X) :- is_int(Y), false,X is Y+1.
请注意,您的程序还剩下很多东西。例如,实际的等式消失了(这或多或少是逻辑部分......)。不过,这个片段是相关的。只要你不改变该片段中的某些内容,问题就会持续存在!对于纯粹的单调程序来说,这是有保证的......
以下是我首选的解决方案:它使用length/2
和between/3
,这是Prolog prologue的两个经常支持的谓词。
pythag2(X,Y,Z,N) :- length(_, N), between(1,N,X), between(1,N,Y), between(1,N,Z), Z*Z =:= X*X + Y*Y.
答案 1 :(得分:3)
我最近也考虑过Prolog解决方案 找到毕达哥拉斯三重奏。我想出了一个略有不同 码。假设我们有一个功能:
isqrt(a) = floor(sqrt(a))
这足以枚举x和y,并检查是否 x * x + y * y是某些z的平方。即检查:
h = x*x+y*y, z = isqrt(h), z*z = h ?
函数isqrt可以通过二分实现。对于 对称性破坏我们可以在x之后枚举y。假设 N = 99,结果代码为:
% between(+Integer, +Integer, -Integer)
between(Lo, Hi, _) :-
Lo > Hi, !, fail.
between(Lo, _, Lo).
between(Lo, Hi, X) :-
Lo2 is Lo+1, between(Lo2, Hi, X).
% bisect(+Integer, +Integer, +Integer, -Integer)
bisect(Lo, Hi, X, Y) :-
Lo+1 < Hi, !,
M is (Lo+Hi) // 2,
S is M*M,
(S > X -> bisect(Lo, M, X, Y);
S < X -> bisect(M, Hi, X, Y);
M = Y).
bisect(Lo, _, _, Lo).
% pythago(-List)
pythago(X) :-
X = [A,B,C],
between(1, 99, A),
between(A, 99, B),
H is A*A+B*B,
bisect(0, H, H, C),
C =< 99, H =:= C*C.
应该有50个这样的毕达哥拉斯三重奏,另见Sloan's A046083:
?- findall(-, pythago(_), L), length(L, N).
N = 52.
有人可能会用以下方法进行交叉检查 CLP(FD)解决方案。
:- use_module(library(clpfd)).
% pythago3(-List)
pythago3(X) :-
X = [A,B,C],
X ins 1..99,
A*A+B*B #= C*C,
A #=< B,
label(X).
它提供了相同数量的解决方案:
?- findall(-, pythago3(_), L), length(L, N).
N = 50.