我正在使用SWI Prolog学习Prolog,我对如何在Prolog中实现 2-3 dictionary 的工作有所怀疑。
我知道 2-3词典的理论是树,其内部节点可以生成2或3个具有以下属性的子树:
1)所有商品都存储在树叶中,并从较小的订购到较大的商品
2)所有树叶都处于同一水平
3)内部节点不包含插入的项目,但包含以下列方式指定子树的最小元素的标签:
因此,搜索这种词典中是否存在一个项目非常简单。
插入更复杂,我理解它的理论,但我对Prolog的解释只有一些问题。
这是我的程序(摘自Ivan Bratko的书:人工智能编程):
/* in(Item, Tree) predicate is TRUE if the searched Item is in the specified Tree */
% BASE CASE: Item found in a leaf, so end
in(Item, l(Item)).
/* CASE 1: I am searching Item in an internal node having a single label value
so this internal node have 2 subtrees
*/
in(Item, n2(T1,M,T2)) :- gt(M,Item), % IF M label value lexicographically follows the searched Item value
!,
in(Item,T1) % THEN search Item in the left subtree
; % (; is an OR)
in(Item,T2). % Otherwise search Item in the right subtree
/* CASE 2: I am searching Intem in an internal node having 2 label values so this
internal node have 3 subtrees
*/
in(Item, n3(T1,M2,T2,M3,T3)) :- gt(M2,Item), % IF M2 label value lexicographically follows the searched Item value
!,
in(Item,T1) % THEN search Item in the left subtree
; % (; is an OR)
/* IF M3 label value lexicographically follows the searched Item value
BUT this is NOT TRUE that M2>Item
*/
gt(M3,Item),
!,
in(Item,T2) % THEN search Item in the central subtree
; % (; is an OR)
in(Item,T3). % ELSE (it is TRUE that Item>M3) search in the right subtree
/*
*/
% Insertion in the 2-3 dictionary
/* Add X to Tree giving Tree1
CASE 1: After the insertion of X into Tree, Tree1 does not grow upwards (so it means that an internal nodes
having 2 subtrees child, now have 3 subtrees child, so the original tree has grown in width)
*/
add23(Tree, X, Tree1) :- ins(Tree, X, Tree1).
/* CASE 2: Tree grows upwards: It means that if after the insertion of X the height of the new tree is
increased so the ins/5 predicate determines the two subtrees T1 and T2 which are then combined
into a bigger tree
*/
add23(Tree, X, n2( T1, M2, T2)) :- ins(Tree, X, T1, M2, T2).
del23(Tree, X, Tree1) :- add23(Tree1, X, Tree). % Delete X from Tree giving Tree1
/* BASE CASE: Inserting the X item into a voil (nil) tree means to obtain a tree
consisting of the single new leaf l(X)
*/
ins(nil, X, l(X)).
/* BASE CASES: related to inserting a new item X into a tree composed by
a single leaf
*/
ins(l(A), X, l(A), X, l(X)) :- gt(X, A).
ins(l(A), X, l(X), A, l(A)) :- gt(A, X).
/* Tree = n2(T1, M , T2) so Tree is a tree having 2 subtrees T1 and T2
M: is the MINIMAL ELEMENT OF T2
IF it is TRUE that M>X (the minimal element in T2 is > the new X item)
I have to insert X in the LEFT subtrees (into T1 that becomes NT1 and
now have 2 leaves)
*/
ins(n2(T1, M , T2), X, n2(NT1, M, T2)) :- gt(M, X),
ins(T1, X, NT1).
/* Tree = n2(T1, M , T2) so Tree is a tree having 2 subtrees T1 and T2
M: is the MINIMAL ELEMENT OF T2.
IF it is TRUE that M>X (the minimal element in T2 is > the new X item) and
IF I can insert
*/
ins(n2(T1, M, T2), X, n3(NT1a, Mb, NT1b, M, T2)) :- gt(M, X),
ins(T1, X, NT1a, Mb, NT1b).
ins(n2(T1, M, T2), X, n2(T1, M, NT2)) :- gt(X, M),
ins(T2, X, NT2).
ins( n2( T1, M, T2), X, n3( T1, M, NT2a, Mb, NT2b)) :-
gt( X, M),
ins( T2, X, NT2a, Mb, NT2b).
ins( n3( T1, M2, T2, M3, T3), X, n3( NT1, M2, T2, M3, T3)) :-
gt( M2, X),
ins( T1, X, NT1).
/* Tree = n3(T1, M2, T2, M3, T3) so Tree is a tree having 3 subtree: T1,T2 and T2
and the ROOT of Tree is the node (M2,M3)
M2: MINIMAL ELEMENT of T2 subtree
M3: MINIMAL ELEMENT of T3 subtree
If I had the item X then Tree have to grow in height
IF it is TRUE that M2 > X I could try to insert the X item into T1 subtree
IF it is TRUE that X is added in T1 and T1 is splitted in NT1a and NT1b having root Mb
so the new tree is: n2(NT1a, Mb, NT1b), M2, n2(T2, M3, T3)
*/
ins(n3(T1, M2, T2, M3, T3), X, n2(NT1a, Mb, NT1b), M2, n2(T2, M3, T3)) :-
gt(M2, X),
ins(T1, X, NT1a, Mb, NT1b).
ins(n3(T1, M2, T2, M3, T3), X, n3(T1, M2, NT2, M3, T3)) :-
gt(X, M2), gt(M3, X),
ins(T2, X, NT2).
ins( n3( T1, M2, T2, M3, T3), X, n2( T1, M2, NT2a), Mb, n2( NT2b, M3, T3)) :-
gt( X, M2), gt( M3, X),
ins( T2, X, NT2a, Mb, NT2b).
ins( n3( T1, M2, T2, M3, T3), X, n3( T1, M2, T2, M3, NT3)) :-
gt( X, M3),
ins( T3, X, NT3).
ins( n3( T1, M2, T2, M3, T3), X, n2( T1, M2, T2), M3, n2( NT3a, Mb, NT3b)) :-
gt( X, M3),
ins( T3, X, NT3a, Mb, NT3b).
在这个实现中,我有:
第一个疑问与 add23 和 ins 谓词之间存在的关系有关:
/* Add X to Tree giving Tree1
CASE 1: After the insertion of X into Tree, Tree1 does not grow upwoards (so it means that an internal nodes
having 2 subtrees child, now have 3 subtrees child, so the original tree has grown in width)
*/
add23(Tree, X, Tree1) :- ins(Tree, X, Tree1).
/* CASE 2: Tree grows upwards: It meaans that if after the insertion of X the height of the new tree is
increased so the ins/5 predicate determines the two subtrees T1 and T2 wich are then combined
into a bigger tree
*/
add23(Tree, X, n2( T1, M2, T2)) :- ins(Tree, X, T1, M2, T2).
正如在评论中写的那样,我认为第一个与新的树不会长大的情况有关(例如,我有一棵树,有2个子树,插入一个新项目,我是创建一个新的子树,其叶子处于同一级别所有其他子叶子(因此内部节点现在将有2个标签)是不是?
所以在逻辑中我可以把它读作:如果 ins(Tree,X,Tree1)谓词为TRUE,那么我将X添加到Tree,生成具有相同高度的新Tree1树,但包含X项(因此它必须有一个内部节点有3个孩子)
第二种情况与案例有关,我有一棵树,我必须在已经有3个子树的节点中插入一个新项目,所以我必须将两棵树中的旧树分开作为标签从旧原始节点的2个标签中获取单个标签。所以我可以插入新项目作为叶子和新树的新根。
所以在逻辑上我可以把它读作:
如果谓词 ins(树,X,T1,M2,T2)为TRUE则表示谓词为TRUE: add23(树,X,n2(T1, M2,T2))
我将原始树Tree(具有 3个子树)拆分为两个不同的树:T1和T2,以root为单位的节点具有单个最小标签原树树的旧根。
所以碰巧我肯定会有一个树有两个子树,另一个有一个子树,所以我可以将新插入的项添加到这个子树中,并将它作为newtree的新根用高度增加一级。
是不是?我不确定这个...
在这本书中,我找到了对 ins 谓词的三个具体案例的解释,我对它的工作方式有很多疑问:
案例1:
ins(n2(T1, M , T2), X, n2(NT1, M, T2)) :- gt(M, X),
ins(T1, X, NT1).
这个案例告诉我原始树是: n2(T1,M,T2)我将X插入树中,这是一棵只有 2个子树:T1和T2。
M 是右边的最低元素
因此,如果 gt(M,X)为TRUE,则表示M> X,因此这意味着X可能是 LEFT SUBTREE ,变为 NT1 < / strong>(为什么?它可能取决于旧T1在其子树中只有一片叶子的事实?)因此,最后,原始树变为 n2(NT1,M,T2)< /强>
我认为这种情况很简单
案例2:
ins(n2(T1, M, T2), X, n3(NT1a, Mb, NT1b, M, T2)) :- gt(M, X),
ins(T1, X, NT1a, Mb, NT1b).
此案例告诉我,原始树是: n2(T1,M,T2),我将X插入树只是一棵树 2个子树:T1和T2。
M 是右边的最低元素
如果它是真的M> X并且这是正确的谓词: ins(T1,X,NT1a,Mb,NT1b)
这意味着我将T1拆分为2个子树NT1a和NT1b将第三个子项添加到原始树 n3(NT1a,Mb,NT1b,M,T2)
好的,这很清楚,但我的问题是:在完整的代码中,这个谓词必须要满足什么?我在混淆......
案例3:
ins(n3(T1, M2, T2, M3, T3), X, n2(NT1a, Mb, NT1b), M2, n2(T2, M3, T3)) :-
gt(M2, X),
ins(T1, X, NT1a, Mb, NT1b).
这种情况是原始树有三个子树的情况,当我必须插入它时,我必须将原始树分成两个树(n3(T1,M2,T2,M3,T3)和n2( NT1a,Mb,NT1b)),将新项X插入其中一个子树中,并使用第二个子树的最小元素作为左子树的根作为新树的根(现在高于一个级别)
我的疑问是:在前一种情况下, NewTree 是 n2(NT1a,Mb,NT1b),M2,n2(T2,M3,T3)
所以M2(右子树的最小元素)是新根,因为它是真的,M2> X?
你能给我一些提示来更好地理解这些东西吗?
答案 0 :(得分:1)
首先,有几个风格点。您不需要为n2
和n3
设置单独的构造函数,因为arities将为您处理。其次,你应该怀疑任何削减它的谓词,特别是你在/ 2中使用的红色削减。通常更好(和更快)地进行明确的案例分析。另外,如果可以的话,在第一个参数中进行比较,并将其作为默认情况下的索引。
我在/ 2中重写为
in(leaf(Item), Item).
in(node(Left, M, Right), Item):-
compare(Order, Item, M),
in_2(Order, Item, node(Left, M, Right).
in(node(Left, M1, Mid, M2, Right), Item):-
compare(Order, Item, M1),
in_3(Order, Item, node(Left, M1, Mid, M2, Right)).
in_2(<, Item, node(Left, _, _)):-
in(Left, Item).
in_2(=, Item, node(_, _, Right)):-
in(Right, Item).
in_2(>, Item, node(_, _, Right)):-
in(Right, Item).
in_3(<, Item, node(Left, _, _, _, _)):-
in(Left, Item).
in_3(=, Item, node(_, _, Mid, _, _)):-
in(Mid, Item).
in_3(>, Item, node(Left, M1, Mid, M2, Right)):-
compare(Order, Item, M2),
in_3a(Order, Item, node(Left, M1, Mid, M2, Right)).
in_3a(<, Item, node(_, _, Mid, _, _)):-
in(Mid, Item).
in_3a(=, Item, node(_, _, _, _, Right)):-
in(Right, Item).
in_3a(>, Item, node(_, _, _, _, Right)):-
in(Right, Item).
至于树插入,它比我想象的要复杂得多。
2-3棵树的一个关键特征是所有叶子都处于相同的深度。这意味着,当您插入项目时,您必须遍历树以查找需要插入树叶的位置,然后将更改传播回树中以确保其保持平衡。这些slides from a data structures lecture概述了算法。尝试将它们翻译成Prolog。幻灯片清楚地列出了不同的案例。我上面给出的示例代码应该可以帮助您完成插入算法的第一阶段(找到正确的底层节点来插入新的叶子)。当你重新开始工作时,采用相同的方法。