在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 /计算机应该能够毫无问题地处理这个问题,这意味着我需要重新考虑算法?
答案 0 :(得分:5)
@Will Ness已经给了你一个fine definition for perm/2
。但是让我们来看看你的节目。事实上,你有非常奇怪的行为:有时似乎有效,有时则不行。我们怎样才能缩小范围呢?跟踪似乎不是一种选择,因为您已经体验过自己。
这是一种特殊的调试技术,称为切片。我会通过插入目标 false
来修改您的程序以查看正在发生的事情。生成的程序称为failure slice。我将使用查询:
?- mysort([1],[2|_]).
Fatal Error: global stack overflow
显然,具有单个1
元素的列表不能与以2
开头的列表相对应。理想情况下,这应该失败。它不能 复杂。可以吗?
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。