这个prolog排序程序是否仅仅因为其复杂性而溢出堆栈 - 或者因为它是错误的?

时间:2013-07-05 21:55:39

标签: prolog failure-slice

previous post中,我最终想出了如何编写gprolog程序来检查一个列表是否是另一个列表的排列。据我所知,它有效。

现在,我正在创建一个mysort谓词,它将排列谓词与这个谓词组合在一起(据我所知也是如此):

sorted([]).   
sorted([L]) :- !.  
sorted([L|[T|TT]]) :- L @=< T, sorted([T|TT]).

由于我的原始perm谓词被设计为在得到答案后立即以!终止,因此我做了一些修改以允许mysort检查可能性。以下是mysort,其特殊backtrack_perm,以及与旧perm的重叠(我只是将其修改为对backtrack_perm的轻微更改):

perm([],[]).
perm([LH|LT],R) :-
   backtrack_perm([LH|LT],R),
   !.

perm_recurse([],X). 
perm_recurse([LH|LT],R) :-
   member(LH,R),
   select(LH,[LH|LT],X),
   select(LH,R,Y),
   perm_recurse(X,Y),
   !. 

mysort(L,M) :-
   backtrack_perm(L,M),
   sorted(M),
   !.  

backtrack_perm([],[]).
backtrack_perm([LH|LT],R) :-
   length([LH|LT],A),
   length(R,B),
   A == B,
   member(LH,R),
   select(LH,[LH|LT],X),
   select(LH,R,Y),
   perm_recurse(X, Y).  

虽然它的组件看起来像上面提到的那样正常,但mysort会导致某些输入上的堆栈溢出,例如mysort([5,3,2],X)。在已经排序的列表中,例如mysort([2,3,5],X),或者甚至是像mysort([3,2,5],X)这样的部分列表,跟踪可能很长,但它确实得到了答案。正因为如此 - 并且因为像[2,1]这样的小型完全向后的列表工作得很好 - 我认为问题只是过程本身在所有这些排列中太空/耗时。

如果没有将太多深深地插入更长的痕迹,那么可以安全地假设是这种情况吗?或者Prolog /计算机应该能够毫无问题地处理这个问题,这意味着我需要重新考虑算法?

1 个答案:

答案 0 :(得分:5)

@Will Ness已经给了你一个fine definition for perm/2。但是让我们来看看你的节目。事实上,你有非常奇怪的行为:有时似乎有效,有时则不行。我们怎样才能缩小范围呢?跟踪似乎不是一种选择,因为您已经体验过自己。

这是一种特殊的调试技术,称为切片。我会通过插入目标 false 修改您的程序以查看正在发生的事情。生成的程序称为failure slice。我将使用查询:

?- mysort([1],[2|_]).
Fatal Error: global stack overflow

显然,具有单个1元素的列表不能与以2开头的列表相对应。理想情况下,这应该失败。它不能 复杂。可以吗?

@larsmans已经给了你一个提示,但我会自己尝试一下。我将在您的计划中添加以下目标 false 。以这种方式,某些部分将永远不会被调用,它们会被>击穿。并且根本不会调用某些谓词,因此我将不再显示它们:

?- mysort([1],[2|_]).

mysort(L,M) :-
   backtrack_perm(L,M), false,
   sorted(M),
   !.  

backtrack_perm([],[]) :- false.
backtrack_perm([LH|LT],R) :-
   length([LH|LT],A),
   length(R,B), false,
   A == B,
   member(LH,R),
   select(LH,[LH|LT],X),
   select(LH,R,Y),
   perm_recurse(X, Y).  

这个故障片在某种意义上和你的程序一样糟糕:再次,它不会终止。但它更小。要解决这个问题,你必须在该片段中做一些事情。因为,只要该部分保持不变,问题就会持续存在。

在这种情况下,目标length(R,B)是罪魁祸首:变量B首次出现在此处,因此未经实例化。并且R也未被实例化。目标length(R, B)的答案是什么?试试吧!

| ?- length(R, B).

B = 0
R = [] ? ;

B = 1
R = [_] ? ;

B = 2
R = [_,_] ? ;

B = 3
R = [_,_,_] ? ;

B = 4
R = [_,_,_,_] ? ;

B = 5
R = [_,_,_,_,_] ? ...

所以答案无限多。因此,您的程序不会终止。

通过length(R, B), A == B替换length(R, A)可以轻松解决此问题。

| ?- mysort([9,1,2,3,4,5,6,7,8],L).

L = [1,2,3,4,5,6,7,8,9]

(21841 ms) yes

更多评论:程序中的!是红色削减,它们可能会给您带来很多麻烦。然后,执行时间不是那么好:排序9个元素的21秒听起来不是很酷。但请记住,您的描述基本上是这样说的:我想要一种排列,即提升。你不要说更多。鉴于信息十分稀缺,Prolog至少能够找到正确的答案。它甚至可以节省空间。

如何将此程序变为更有效的程序see this answer