在SWI Prolog结果中生成动态大小置换,错误为:超出全局堆栈

在SWI Prolog结果中生成动态大小置换,错误为:超出全局堆栈,prolog,clpfd,Prolog,Clpfd,我正在尝试使用以下代码生成多个排列: :- use_module(library(clpfd)). p(N, Indexes) :- M in 1..N, M #=< N, length(Indexes, M), Indexes ins 1..N. :-使用_模块(库(clpfd))。 p(N,索引):- 我在1..N, M#=

我正在尝试使用以下代码生成多个排列:

:- use_module(library(clpfd)).

p(N, Indexes) :-
    M in 1..N,
    M #=< N,
    length(Indexes, M),
    Indexes ins 1..N.
:-使用_模块(库(clpfd))。
p(N,索引):-
我在1..N,
M#=

它返回所有结果,但最终崩溃,出现
错误:超出全局堆栈

代码中发生的情况是,通过调用
length/2
隐式设置列表的长度。这将从长度为0的列表开始,该列表将
M
绑定到0,从而唤醒1..N
中的约束
M,该约束失败。接下来,它返回一个长度为1的列表,该列表成功,然后回溯一个长度为2的列表,该列表再次成功。在此之后,任何进一步回溯到
length/2
的操作都会返回越来越长的列表,但是在1..N
中唤醒
M总是会失败,直到列表变得太大以至于内存不足为止

您需要做的是将choicepoint放在
length/2
调用之前,而不是放在调用内部,例如替换

M #=< N,
这将为您提供:

[debug] [1]  ?- p(2,I).
I = [_G3025],
_G3025 in 1..2 ;
I = [_G3102, _G3105],
_G3102 in 1..2,
_G3105 in 1..2.

[debug] [1]  ?- 

正如twinterer已经回答了这个问题,我只是添加了一种更简单的方法来获得(正确的)排列(可能有用吗?)

测试:


我不清楚你所说的多重排列是什么意思。但是,您的程序可能应该从/3
之间的
开始,然后包含
所有不同的/1
约束

p(N, Indices) :-
    between(1,N,M), % or maybe rather between(0,N,M).
    length(Indices, M),
    Indices ins 1..N,
    all_different(Indices).

然后,使用
labeling/2
生成实际的解决方案。

条件'M#=M
变为0?我还尝试了读取
M#>=
,这也是多余的,没有帮助。我认为
M ins 1..N
类似于循环,但我假设只有当我们强制它位于域中时它才会变成循环。@ukasz:M从0开始,正如twinterer(谢谢!+1)清楚地解释的那样。尝试此查询:?-length(X,Y)。@ukasz:
M ins 1..N
不是一个循环,它是一个限制
M
的域在1和N之间的约束。作为一个约束,它将被挂起,直到
M
的边界发生变化,这时它将被唤醒并检查其一致性
length/2
不是约束,即不考虑变量
M
的域。使用未实例化的变量调用
length/2
,将返回一个空列表。这将
M
绑定到0,从而更改
M
域的边界,从而唤醒发布在
M
上的约束。约束然后检查其一致性并失败,因为0不在域中。+1是更好的解决方案,而不仅仅是修复错误。(应该是
索引1..N
但是)@twinterer:谢谢,修正了!
permutations_n(N, P) :-
  numlist(1, N, L),
  permutation(L, P).
?- permutations_n(3, X).
X = [1, 2, 3] ;
X = [1, 3, 2] ;
X = [2, 1, 3] ;
X = [2, 3, 1] ;
X = [3, 1, 2] ;
X = [3, 2, 1] ;
p(N, Indices) :-
    between(1,N,M), % or maybe rather between(0,N,M).
    length(Indices, M),
    Indices ins 1..N,
    all_different(Indices).