使用clpfd的SWI-Prolog堆栈限制超出了很小的问题空间

isa*_*nat 3 prolog swi-prolog clpfd

我尝试显示这种行为的一个最小的例子。这是我加载的代码:

:- use_module(library(clpfd)).

gcd(A, 0, A) :- !.
gcd(0, B, B) :- !.
gcd(A, B, C) :- A #> B, !, A1 #= A mod B, gcd(A1, B, C).
gcd(A, B, C) :- A #=< B, A1 #= B mod A, gcd(A1, A, C).

ncalc(N, X, Y) :-
    Y #=< N,
    X*X #= (Y),
    X #< Y,
    X #> 0,
    gcd(X, Y, 1).
Run Code Online (Sandbox Code Playgroud)

查询ncalc(9, X, Y).我得到:

ERROR: Stack limit (1.0Gb) exceeded
ERROR:   Stack sizes: local: 1Kb, global: 0.7Gb, trail: 9Kb
ERROR:   Stack depth: 1,548,057, last-call: 100%, Choice points: 5
ERROR:   In:
ERROR:     [1,548,057] clpfd:pop_queue(_176712142, <compound fast_slow/2>, 1)
ERROR:     [1,548,056] clpfd:pop_queue(_176712170)
ERROR:     [1,548,055] clpfd:fetch_propagator(_176712188)
ERROR:     [1,548,054] clpfd:do_queue
ERROR:     [1,548,052] clpfd:parse_clpfd('<garbage_collected>', _176712222)
ERROR:
ERROR: Use the --stack_limit=size[KMG] command line option or
ERROR: ?- set_prolog_flag(stack_limit, 2_147_483_648). to double the limit.
Run Code Online (Sandbox Code Playgroud)

查询ncalc(8, X, Y).立即回报false.。也查询ncalc(9, 1, Y).通过ncalc(9, 8, Y).(X的所有可能有效范围)。为什么它仅适用于较简单的情况?有没有解决方法?谢谢!

PS:我是Prolog的新手,我打算做的事情比较复杂,但是知道了解决方法之后,我可能可以采用该解决方案。

fal*_*lse 5

(让我们忽略编程风格,请参阅@Capelli的评论。)

不终止的实际来源是由于SWI的CLP(FD)系统的弱点。这是实际的罪魁祸首:

?- X in 2..3, Y in 4..9, Y mod X#=X1.
X in 2..3,
Y mod X#=X1,
Y in 4..9.
Run Code Online (Sandbox Code Playgroud)

因此,系统无法推断的任何有用信息X1。将此与SICStus进行对比:

| ?- X in 2..3, Y in 4..9, Y mod X#=X1.
Y mod X#=X1,
X in 2..3,
Y in 4..9,
X1 in 0..2 
Run Code Online (Sandbox Code Playgroud)

在这里,SICStus推论出X1必须在范围之内0..2,因此不能为负。这又避免了在下一个推论中所讨论的循环。

您可以通过坚持使用非负数来对系统有所帮助,因此模块始终小于操作数。

A1 #>= 0, A1 #=< B, A1 #=< A

同时固定在CLP(Z)中

  • 好赶上!library(clpfd)需要很多维护工。前段时间我发现了一个测试用例,其中all_distinct的性能比all_different差15倍。 (3认同)