【问题标题】:Reversible tree length relation可逆树长关系
【发布时间】:2012-12-30 23:30:02
【问题描述】:

我正在尝试在“纯”Prolog 中编写可逆关系(没有 is、cut 或类似的东西。是的,这是家庭作业),我必须承认我不知道如何做。我没有看到任何创建这种东西的过程。

我们被赋予了“不纯”但可逆的算术关系(add、mult、equal、less、...),我们必须使用它来创建这些关系。

现在我正在尝试通过创建关系 tree(List,Tree) 来了解如何创建可逆函数,如果 List 是二叉树 Tree 的叶子列表,则这是真的。

为了实现这样的事情,我正在尝试创建tree_size(Tree,N) 关系,当Tree 有N 离开时,这是正确的。这是我天真的、不可逆的关系:

tree_len(n(_,leaf,leaf),1).
tree_len(n(op,G,D),N) :-
    tree_len(G,TG),
    tree_len(D,TD),
    add(TG,TD,N).

我可以查询tree_len(some tree, N),但不能查询tree_len(X,3),所以它是不可逆的。到目前为止,我已经尝试了一些事情,但我必须承认我感到沮丧,因为我不知道在哪里寻找什么。真的有办法做到这一点吗?

【问题讨论】:

    标签: prolog clpfd failure-slice successor-arithmetics


    【解决方案1】:

    不终止的原因

    首先,让我们尝试理解为什么您的定义是不可逆的。我将使用failure-slice 来更好地解释它。所以考虑查询tree_len(X,1). 乍一看,一切都很完美,你甚至会得到一个很好的答案!

    ?- tree_len(T,1).
    T = n(_G267, leaf, leaf) 
    

    但永远不要要求另一个答案,因为它会循环:

    ?- tree_len(T,1).
    T = n(_G267, leaf, leaf) ;
    ** LOOPS **
    

    所以我们得到的答案有点让人分心。乍一看似乎一切正常,但只有在回溯时才遇到真正的问题。这是在 Prolog 中习惯的东西。显然,您使用 3 的查询得到了更好的选择。但那是运气。

    一般来说,有一种简单的方法可以确保这一点。只需将额外的目标 false 添加到查询中。添加false 意味着我们不再对任何答案感兴趣,因为它不再能够成功。这样一来,所有的干扰都消除了,我们直接面对问题:

    ?- tree_len(T,1), false.
    ** LOOPS **
    

    那么,这个循环从何而来?

    在纯粹的、单调的 Prolog 程序(例如这个)中,我们可以通过在我们的程序中添加一些目标 false 来定位不终止的原因。如果生成的程序(称为failure-slice)没有终止,那么原始程序也不会终止。这是我们查询的最小故障片:

    ?- tree_len(T,1), 假。 tree_len(n(_,leaf,leaf),1) :- false。 tree_len(n(op,G,D),N) :- tree_len(G,TG), 假, tree_len(D,TD), N 是 TG+TD。

    我们的程序所剩无几!正是这个微小的片段负责不终止。如果我们想解决这个问题,我们必须在那个微小的部分做点什么。其他一切都是徒劳的。

    所以我们需要做的是以某种方式改变程序,使这个片段不再循环。

    实际上,我们有两个选择,我们可以使用successor arithmetics 或constraints like clpfd。前者在任何 Prolog 系统中都可用,后者仅在 SWI、YAP、SICStus、GNU、B 等系统中提供。

    使用successor-arithmetics

    现在,3 由s(s(s(0))) 表示。

    tree_lensx(T, s(N)) :- 树_lendiff(T,N,0)。 tree_lendiff(n(_,leaf,leaf), N,N)。 tree_lendiff(n(op,G,D), s(N0),N) :- tree_lendiff(G, N0,N1), tree_lendiff(D, N1,N)。

    我在这里使用了几种常见的编码技术。

    区别

    实际的关系是tree_lendiff/3,它不是用一个参数表示一个自然数,而是用两个。实际数字是两者之间的差异。通过这种方式,可以保留可逆的定义。

    避免左递归

    另一种技术是避免左递归。 tree_lendiff/3 描述的长度实际上是长度减一。还记得我们首先得到的故障片吗?同样的故障片也会出现在这里!然而,通过将长度“移动”一,递归规则的头部现在可以确保终止。

    使用library(clpfd)

    最初,开发有限域上的约束是为了解决组合问题。但是您也可以使用它们来获得可逆算术。 SWI 和 YAP 中的实现甚至可以编译成代码,其效率通常与传统的不可逆 (is)/2 相当,同时仍然是可逆的。

    :- use_module(library(clpfd)).
    
    tree_fdlen(n(_,leaf,leaf),1).
    tree_fdlen(n(op,G,D),N) :-
       N #= TG+TD,
       TG #>= 1,
       TD #>= 1,
       tree_fdlen(G,TG),
       tree_fdlen(D,TD).
    

    此程序更符合您的原始定义。尽管如此,请注意TG #>= 1 和TD #>= 1 这两个目标添加了冗余信息以确保终止该程序。

    我们现在可以像这样枚举一定范围内的所有树:

    ?- Size in 0..4, tree_fdlen(T, Size).
    Size = 1, T = n(_A,leaf,leaf) ;
    Size = 2, T = n(op,n(_A,leaf,leaf),n(_B,leaf,leaf)) ;
    Size = 3, T = n(op,n(_A,leaf,leaf),n(op,n(_B,leaf,leaf),n(_C,leaf,leaf))) ;
    Size = 4, ... ;
    Size = 4, ... ;
    Size = 3, ... ;
    Size = 4, ... ;
    Size = 4, ... ;
    Size = 4, ... ;
    false.
    

    注意答案替换的确切顺序!它不仅仅是 1,2,3,4!而是以一些的顺序找到答案。哪一种都无所谓,只要我们对找到所有解决方案感兴趣!

    【讨论】:

    • 谢谢!这真的帮助我理解了我的错误。我还有一个小问题,实现可逆性的一般规则是避免潜在的无限生成子句,还是让它们进入然后“扩展”它们?
    • @Manux:如果解决方案的集合是无限的并且只能由无限多个答案来描述,那么您无法避免无限生成子句。所以没有办法绕过这些条款。如何处理它们的一般规则很难制定。我宁愿使用失败切片作为一种快速根除绝望案例的方法,让你的直觉来做剩下的事情。您还可以查看failure-slice 的其他案例。 Like this one
    【解决方案2】:

    有趣的问题。

    这就是我要做的。基本上,您的关系是不可逆的,因为 add/3 不是。我本质上所做的是,用与叶子数量相对应的大小列表来代替计数 - 是可逆的(嗯,append/3 和 length/2 是可逆的)。

    这是您需要的吗?发布的代码可以在 YAP 下运行。

    PS:这可能不是最简洁的解决方案,但这是我的想法。如果您有任何其他问题,我会尽力提供帮助。

    :-  use_module(library(lists)).
    
    do_tree_len(n(_,leaf,leaf), [X]).
    do_tree_len(n(op,G,D), [X1,X2|T]) :-
        append(TG, TD, [X1,X2|T]),
        TG \= [X1,X2|T], % To prevent infinite loops, when TG or TD is []
        TD \= [X1,X2|T],
        do_tree_len(G, TG),
        do_tree_len(D, TD).
    
    tree_len(Tree, N):-
        length(L, N),
        do_tree_len(Tree, L).
    

    【讨论】:

    • 我看到这个解决方案的问题是查询现有树的tree_len 会给出很好的答案,但如果再次询问则会循环。
    • 你说问题是add/3。但这不是真的:即使 add/3 是可逆的(就像您使用 TG+TD#=N 时一样),您仍然会遇到终止问题。
    • @false 感谢您指出这一点。我也很喜欢阅读您的回答:)。
    猜你喜欢
    • 1970-01-01
    • 2017-10-21
    • 1970-01-01
    • 1970-01-01
    • 2011-07-31
    • 1970-01-01
    • 2017-05-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多