查找数字是否为另一个的倍数

时间:2019-04-20 09:50:16

标签: prolog failure-slice successor-arithmetics

查看下面的代码:

multiple(X,0).
multiple(X,Y) :- lt(0,X), lt(0,Y), diff(Y,X,D), multiple(X,D).

碰巧有问题。供您参考:
 lt / 2是第一个参数是否小于第二个参数。
diff / 3是第三个参数是否等于第一个参数减去第二个参数。
lt / 2和diff / 3正确定义。

定义中是否存在逻辑错误?是假设0是每个数字的倍数是有问题的,还是其他地方的逻辑错误?我得到正确答案,但是查询进入无限循环,我认为。

编辑:
这是其他定义。

natNum(0).
natNum(s(X)) :- natNum(X).

lt(0,s(X)) :- natNum(X).
lt(s(X),s(Y)) :- lt(X,Y).

sum(0,X,X).
sum(s(X),Y,s(Z)) :- sum(X,Y,Z).

diff(X,Y,Z) :- sum(Z,Y,X).

?- multiple(X, s(s(s(s(s(s(0))))))).

其中s(0)是1,s(s(0))是2,依此类推。它给出了X的所有所需答案,但是在最后一个答案之后,它被卡住了。我假设处于无限递归循环中?

1 个答案:

答案 0 :(得分:3)

您的程序中正在发生什么?它会永远循环吗?或者由于您最近几十年来没有更新硬件而只花了一些时间?我们不能说。 (实际上,我们可以通过查看您的程序来判断,但这暂时太复杂了。)

我们可以轻松完成的工作就是缩小这种昂贵工作的来源。而且,这对您的程序没有深入的了解。让我们从查询开始:

| ?- multiple(X, s(s(s(s(s(s(0))))))).
X = s(0) ? ;
X = s(s(0)) ? ;
X = s(s(s(0))) ? ;
X = s(s(s(s(s(s(0)))))) ? ;
** LOOPS or takes too long ***

没有更简单的方法来做到这一点吗?所有这些分号输入。相反,只需将 false 添加到查询中即可。通过这种方式,不再显示找到的解决方案,我们可以集中精力处理这种令人讨厌的循环。而且,如果需要的话,您还可以在程序中添加 false 个目标!通过这样的目标,推理的数量可能会减少(或保持不变)。如果生成的片段(称为)正在循环,则这是原始程序循环的原因

multiple(_X,0) :- false.
multiple(X,Y) :- lt(0,X), false, lt(0,Y), diff(Y,X,D), multiple(X,D).

natNum(0) :- false.
natNum(s(X)) :- natNum(X), false.

lt(0,s(X)) :- natNum(X), false.
lt(s(X),s(Y)) :- false, lt(X,Y).

?- multiple(X, s(s(s(s(s(s(0))))))), false.
** LOOPS ***

您识别您的程序吗?仅剩下循环所需的那些部分。而且,实际上在这种情况下,我们有一个无限循环。

要解决此问题,我们需要在其余可见部分中进行一些修改。我会去寻找lt/2的第一个子句可以概括为lt(0, s(_))的地方。

但是等等!为什么可以概括我们有一个自然数的要求呢?查看您所写的事实multiple(X,0).。您也没有要求X是自然数。这种过度概括通常出现在Prolog程序中。它们以相对较低的价格改善了端接属性:有时它们过于笼统,但所有适合泛化的术语都不是自然数。它们是any[a,b,c]之类的术语,因此,如果它们出现在某个位置,则说明它们不属于解决方案。


因此,我们的想法是将 false 个目标放入您的程序中,以使生成的程序(失败片段)仍然循环。在最坏的情况下,您将 false 放在错误的位置,程序将终止。通过反复试验,您可以获得最小的失败切片。现在所有经过的东西都是不相关!特别是diff/3。因此(暂时)无需了解它。看剩下的程序就足够了。

相关问题