在序言中表达 collatz 猜想的不同方式失败了

Different ways of expressing collatz conjecture in prolog fail

提问人:Joseph Garvin 提问时间:2/7/2023 最后编辑:falseJoseph Garvin 更新时间:2/9/2023 访问量:215

问:

我正在使用 SWI Prolog 和此处的教程学习 prolog。我发现,如果我像他们在视频中所做的那样表达 collatz 猜想,只要我替换我猜想是 vs 差异,它就会起作用。但是,如果我调整定义,它似乎会破裂,要么是错误的,要么是错误的结论。为什么我的替代定义失败?法典:#=isswiplscryer-prolog

use_module(library(clpfd)).

%% Does work, collatz_next(A, 1) gives A=2
collatz_next(N0, N) :-
    N0 is 2*N.
collatz_next(N0, N) :-
   N0 is 2*_ + 1,
   N is 3*N0 + 1.

%% Doesn't work, collatz_next(A, 1) gives false
%% collatz_next(N0, N) :- ((N0 mod 2) is 0),((N0 / 2) is N).
%% collatz_next(N0, N) :- ((N0 mod 2) is 1),((N0 * 3 + 1) is N).

%% Doesn't work, collatz_next(A, 1) gives false
%% collatz_next(N0, N) :- ((N0 mod 2) is 0),(N0 is 2*N).
%% collatz_next(N0, N) :- ((N0 mod 2) is 1),((N0 * 3 + 1) is N).

%% Doesn't work
%% "Arguments are not sufficiently instantiated"
%% collatz_next(N0, N) :-
%%    N0 / 2 is N.
%% collatz_next(N0, N) :-
%%    N0 is 2*_ + 1,
%%    N is 3*N0 + 1.
Prolog CLPFD Collatz

评论

4赞 brebs 2/7/2023
请参阅 swi-prolog.org/man/clpfd.html 及其行::- use_module(library(clpfd)).
2赞 TessellatingHeckler 2/7/2023
N0 / 2 is N“是”不是这样工作的;答案在左边,算术术语在右边,只有。
2赞 Guy Coder 2/7/2023
这可能是你不知道的历史被迫重温的问题之一。在本例中,Markus 用于在 SWI-Prolog 中发布他的约束库。现在,它们使用 Scryer 发布。您必须在 Github 上检查他保持 SWI-Prolog 版本更新的程度。此外,马库斯不再在这里回答问题。您应该将问题直接发布到他网站上注明的电子邮件中。如果我说错了什么,那就是错误,而不是故意。
0赞 Joseph Garvin 2/8/2023
@TessellatingHeckler 由于我在前两个注释掉的实现中没有收到错误,因此我假设它按该顺序排列时仍然“意味着”一些有效的东西,即使它不是我的意思。交换订单时的意义有什么区别?
2赞 brebs 2/8/2023
@JosephGarvin 你一开始就忽略了基本字符,让它成为一个命令而不是一个事实。:-

答:

5赞 tas 2/8/2023 #1

正如 brebs 已经在评论中所写的那样,如果您使用的是 SWI-Prolog,则必须包含该行。但是,如果您使用的是 Scryer Prolog,则该库称为 clpz,因此您必须包含 .如果您使用各自的库,则谓词(来自链接的视频)适用于视频中描述的两个 Prolog(我使用 Scryer Prolog 版本 0.9.0. 和 SWI-Prolog 版本 8.4.2,均为 Linux 机器上的 64 位)对其进行了测试)。由于评论中尚未提及,我还想向您推荐 The Power of Prolog 中对 CLP(FD) 和 CLP(Z) 的描述:- use_module(library(clpfd)).:- use_module(library(clpz)).collatz_next/2

现在回答你关于失败的替代方案的问题。如果右侧的表达式计算结果为左侧的数字,则内置值为 true。如果左侧是未实例化的变量,则该变量包含调用目标后右侧表达式的值。为了成功,需要对右侧表达式中的所有变量进行状态化。请看以下示例:is/2

?- 3 is 2+1.
true.
   
?- X is 2+1.
X = 3.
   
?- 3 is X+1.
ERROR: Arguments are not sufficiently instantiated
...

?- 3 is 2+X.
ERROR: Arguments are not sufficiently instantiated
...

此外,如果左侧使用浮点数而不是整数实例化,则将失败:is/2

?- 3.0 is 2+1.
false.

现在我们已经介绍了一些基础知识,让我们来看看你的第一个谓词:

%% Does work, collatz_next(A, 1) gives A=2
collatz_next(N0, N) :-
   N0 is 2*N.
collatz_next(N0, N) :-
   N0 is 2*_ + 1,
   N is 3*N0 + 1.

让我们观察一下,虽然你给出的示例产生了一个正确的答案,但在按下 -键后,它会抛出一个实例化错误:;

?- collatz_next(A, 1).
A = 2 ;
ERROR: Arguments are not sufficiently instantiated
...

这是怎么回事?您正在发布查询,因此谓词 head 中的变量与变量统一,变量与 统一。有了这些统一,第一条规则中的唯一目标 ,现在变成了 。这就产生了答案。Prolog 现在尝试第二条规则,其中第一个目标现在变为 。在这里,右侧仍然包含一个变量 (),因此表达式没有充分的状态来计算,因此 Prolog 抛出实例化错误。collatz_next(A, 1).N0AN1N0 is 2*NA is 2*1A = 2collatz_next/2N0 is 2*_ + 1A is 2*_ + 1_

现在让我们尝试反过来使用谓词。正如您在 Youtube 视频中看到的那样,如果那么预期的答案是 .但是,如果您使用谓词进行查询,则不会得到答案和实例化错误:N0=5N=16

?- collatz_next(5, N).
ERROR: Arguments are not sufficiently instantiated
...

查看您的定义,我们可以观察到规则头部的变量与 统一,而第二个参数仍未实例化。因此,第一条规则中的单个目标 ,由于右侧的变量而成为实例化误差。collatz_next/2N05NN0 is 2*N5 is 2*N

请注意,该视频还显示,由于使用了 CLP(FD)/CLP(Z),最一般的查询仍在生成答案,而使用 的版本再次产生实例化错误。:- collatz_next(N0,N).is/2

您发布的接下来的两个版本(带有评论的版本)失败,规则中的第一个目标。由于你查询的规则头部中的变量与变量是统一的,所以所有四个规则中的第一个目标分别变为 和 。如果尝试将这些目标作为查询,答案是错误的:collatz_next/2%% Doesn't work, collatz_next(A, 1) gives false:- collatz_next(A, 1).N0A((A mod 2) is 0)((A mod 2) is 1)

?- ((A mod 2) is 0).
false.

?- ((A mod 2) is 1).
false.

由于规则的第一个目标失败了,Prolog 甚至不会尝试第二个目标,因为一旦你在(链)连词中有一个,它就不能产生真。两个谓词的两个规则都是这种情况,因此您的查询的答案是错误的。另一方面,如果您尝试将左手边换成右手边,您将收到一个实例化错误:falseis/2

?- (0 is (A mod 2)).
ERROR: Arguments are not sufficiently instantiated
...
   
?- (1 is (A mod 2)).
ERROR: Arguments are not sufficiently instantiated
...

也许可以这样想:如果你试图计算一个未实例化的变量模一个实际数字,你会期望会发生什么?一个合理的期望是得到某种反馈,说明这是无法做到的。 另一个合理的观点是期望 Prolog 将发布的目标作为约束进行传播,直到它(希望)在以后得到解决。这基本上就是 CLP(FD)/CLP(Z) 所做的(另请参阅 The Power of Prolog 中关于 CLP(FD) 和 CLP(Z) 中的约束传播部分。只需尝试上述查询:(#=)/2

?- ((A mod 2) #= 0).
A mod 2#=0.            % residual goal
   
?- ((A mod 2) #= 1).
A mod 2#=1.            % residual goal
   
?- (0 #= (A mod 2)).
A mod 2#=0.            % residual goal
   
?- (1 #= (A mod 2)).
A mod 2#=1.            % residual goal

正如你所看到的,已发布的约束现在被传播,并在扣除结束时显示为剩余目标,因为在这些情况下,查询仅由这些单个目标组成,因此无法进一步解决它们。

正如 TessellatingHeckler 在评论中指出的那样,您发布的最后一个版本(标有评论)在第一条规则的单个目标中具有错误的左侧和右侧。但是,即使您交换它们,除非实例化变量,否则您也会收到实例化错误。但即便如此,一旦 Prolog 尝试第二条规则,您仍然会收到实例化错误,因为它的第一个目标包含一个始终未实例化的变量:%% Doesn't workis/2N0N0 is 2*_ + 1_

?- N0 is 2*_ + 1.
ERROR: Arguments are not sufficiently instantiated
...
   
?- 1 is 2*_ + 1.
ERROR: Arguments are not sufficiently instantiated
...

底线是:如果你想使用低级谓词,你必须意识到它们的局限性。如果你想对整数进行声明式推理,你就不能轻易地绕过 CLP(FD)/CLP(Z)。如果您决定使用视频中所示的谓词,则无法将 CLP(FD)/CLP(Z) 约束一对一地交换为低级谓词,例如并期望获得相同的结果。is/2collatz_next/2is/2

评论

1赞 false 2/8/2023
小注释:library(clpz) 在 SICStus 上运行,然后是后来的 Scryer。
0赞 tas 2/8/2023
@false:啊,我不知道。谢谢你指出来。