您的谓词沿一个分支生成无限数量的树的原因是因为您有多个递归,并且像任何语言一样,Prolog 将继续进行它找到的第一个递归调用,直到它返回,在这种情况下,永不。所以你总是在树的一条腿上递归。换句话说,每棵树(左子树和右子树)中至少有两个变量,它们具有无限多的可能性。
二叉树在二维上具有无限的递归可能性。您需要一种使用一维度量对树进行排序的方法。一种这样的指标可能是树中的节点数。如果您按节点计数对树进行排序,从节点计数 0 开始,那么对于每个节点计数,都有有限数量的树要枚举。这是它的一般工作方式:
-
Nodes 是有效的节点数
-
nil 是具有 0 个节点的有效二叉树
-
arbol(_, TL, TR) 是具有 N 节点的有效二叉树,如果 NL 和 NR2 加起来为 N-1 和 TL 是 NL 节点的有效二叉树,并且 TR 是有效二叉树NR 节点的树。由于 Prolog 会在回溯之前从给定点找到所有解决方案,因此它将首先搜索具有给定节点数的所有树,然后再回溯到新的有效节点数。
在 Prolog 中,它看起来像这样。
:- use_module(library(clpfd)).
es_arbol(Tree) :-
length(_, Nodes),
es_arbol(Tree, Nodes).
我使用length/2 来“生成”Nodes 0、1、2 等值。es_arbol(Tree) 将成功生成连续节点计数从 0 开始的二叉树。对于给定的节点计数,@ 987654336@,它将在最终失败之前找到es_arbol(Tree, Nodes)的所有解决方案并再次回溯到length(_, Nodes),这将在Node的下一个值上成功。
es_arbol(nil, 0).
es_arbol(arbol(_,TreeL,TreeR), N) :-
N #> 0,
NL + NR #= N - 1,
NL #>= 0, NR #>= 0,
es_arbol(TreeL, NL),
es_arbol(TreeR, NR).
基本情况是微不足道的。 nil 是具有 0 个节点的树。递归情况表明arbol(_,L,R) 是具有N 节点的二叉树,如果N > 0、NL 和NR 是加起来为N 和TL 和TR 的非负整数是长度分别为NL 和NR 的二叉树。
以上代码运行结果为:
?- es_arbol(Tree).
Tree = nil ;
Tree = arbol(_G258, nil, nil) ;
Tree = arbol(_G17, nil, arbol(_G157, nil, nil)) ;
Tree = arbol(_G17, arbol(_G200, nil, nil), nil) ;
Tree = arbol(_G14, nil, arbol(_G154, nil, arbol(_G593, nil, nil))) ;
Tree = arbol(_G14, nil, arbol(_G154, arbol(_G603, nil, nil), nil)) ;
Tree = arbol(_G14, arbol(_G130, nil, nil), arbol(_G191, nil, nil)) ;
Tree = arbol(_G14, arbol(_G53, nil, arbol(_G193, nil, nil)), nil) ;
Tree = arbol(_G14, arbol(_G53, arbol(_G236, nil, nil), nil), nil) ;
Tree = arbol(_G14, nil, arbol(_G100, nil, arbol(_G214, nil, arbol(_G354, nil, nil)))) ;
Tree = arbol(_G14, nil, arbol(_G100, nil, arbol(_G214, arbol(_G397, nil, nil), nil))) ;
Tree = arbol(_G14, nil, arbol(_G100, arbol(_G216, nil, nil), arbol(_G277, nil, nil))) ;
Tree = arbol(_G14, nil, arbol(_G100, arbol(_G139, nil, arbol(_G279, nil, nil)), nil)) ;
Tree = arbol(_G14, nil, arbol(_G100, arbol(_G139, arbol(_G322, nil, nil), nil), nil)) ;
Tree = arbol(_G14, arbol(_G130, nil, nil), arbol(_G191, nil, arbol(_G664, nil, nil))) ;
Tree = arbol(_G14, arbol(_G130, nil, nil), arbol(_G191, arbol(_G674, nil, nil), nil)) ;
Tree = arbol(_G14, arbol(_G132, nil, arbol(_G272, nil, nil)), arbol(_G676, nil, nil)) .
...
正如@false 在 cmets 中指出的那样,在这种应用枚举约束的情况下,使用 CLP(FD) 并不是最有效的方式。另一种更有效的方法是使用
between/3:
es_arbol(nil, 0).
es_arbol(arbol(_,TreeL,TreeR), N) :-
N > 0,
N1 is N - 1,
between(0, N1, NL),
NR is N1 - NL,
es_arbol(TreeL, NL),
es_arbol(TreeR, NR).