【问题标题】:Prolog: pythagorean triple序言:毕达哥拉斯三元组
【发布时间】:2012-05-09 02:55:16
【问题描述】:

我有这段代码,它使用一个上限变量 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).

【问题讨论】:

    标签: prolog pythagorean failure-slice


    【解决方案1】:

    首先,让我们测试您的解决方案:

    ?- 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. ** 环形 **

    现在,由于目标 false,所有答案都已被删除。剩下的只是最终结果:要么终止,要么不终止,要么出现一些错误。没有其他的。这应该有助于我们对终止的观察——不再有令人眼花缭乱的答案在屏幕上滚动。请注意,这通常不能解决问题。毕竟,我们愿意等待多长时间? 1秒? 1m?

    通过查看相关的故障片,可以最好地理解未终止的实际原因。那是程序的一个片段,其不终止意味着整个程序的不终止。 See this answer for more details。这是您的程序的相关故障片段,用于查询pythag(X,Y,Z,20), false

    毕达格(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), , 减(S,X,S1), X>0, X, 减(S1,Y,Z), Y>0, Yis_int(0) :- 错误. is_int(X) :- is_int(Y),假, X 是 Y+1。

    请注意,您的程序剩下的东西不多。例如,实际的方程式消失了(这或多或少是逻辑部分......)。不过,这个片段是相关的。只要您不更改该片段中的某些内容,问题就会持续存在!对于像这个这样的纯单调程序,这是有保证的......

    这是我的首选解决方案:它使用length/2between/3,这是Prolog prologue 的两个经常支持的谓词。

    pythag2(X,Y,Z,N) :- 长度(_,N), 在(1,N,X)之间, 在(1,N,Y)之间, 在(1,N,Z)之间, Z*Z =:= X*X + Y*Y。

    【讨论】:

    • 谢谢你!信息量很大。想要在没有内置函数的情况下实现这一点,但胜利就是胜利。
    • @RandellK02:length/2between/3 都可以在 Prolog 中实现。但是,有许多微小的棘手案例需要注意。所以最好坚持使用现有的(希望经过测试的)库代码。
    【解决方案2】:

    我最近也在考虑一个 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.
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-08
      • 1970-01-01
      • 1970-01-01
      • 2016-01-17
      相关资源
      最近更新 更多