Algorithm 如何计算树编辑距离?

Algorithm 如何计算树编辑距离?,algorithm,tree,Algorithm,Tree,我需要计算树之间的编辑距离。这篇论文描述了一个算法,但我不能完全理解它。你能用更容易理解的方式描述一个适用的算法吗?伪代码或代码都会有帮助。下面是一些(底部的gzip皮球)用于树编辑距离算法,这些算法可能对您有用 该页面包括参考资料和一些幻灯片,逐步介绍“Zhang and Shasha”算法和其他有用的链接,让您了解最新情况 编辑:虽然此答案被接受,因为它指向Zhang Shasha算法,但链接中的代码存在错误。史蒂夫·约翰逊和蒂姆·塔德已经提供了。有关更多详细信息,请参阅。您可以在 此版本还

我需要计算树之间的编辑距离。这篇论文描述了一个算法,但我不能完全理解它。你能用更容易理解的方式描述一个适用的算法吗?伪代码或代码都会有帮助。

下面是一些(底部的gzip皮球)用于树编辑距离算法,这些算法可能对您有用

该页面包括参考资料和一些幻灯片,逐步介绍“Zhang and Shasha”算法和其他有用的链接,让您了解最新情况


编辑:虽然此答案被接受,因为它指向Zhang Shasha算法,但链接中的代码存在错误。史蒂夫·约翰逊和蒂姆·塔德已经提供了。有关更多详细信息,请参阅。

您可以在
此版本还有一个伪代码。

树编辑距离有许多变体。如果您可以使用自上而下的树编辑距离,这将限制对叶子的插入和删除,我建议尝试以下文章:。实现是一个简单的动态规划矩阵,成本为O(n2)。

(编辑此答案以链接到tim.tadh给出的当前版本的实现)

此Python库正确实现了Zhang Shasha算法:


它最初是当前公认答案中列出的Java源代码的一个直接端口(带有tarball链接的),但该实现不正确,几乎不可能运行。

在这里,您可以找到树编辑距离算法的Java实现:

除了Zhang和Shasha 1989年的算法外,还有最近算法的树编辑距离实现,包括Klein 1998、Demaine等人2009以及Pawlik和Augsten 2011年提出的鲁棒树编辑距离(RTED)算法

我基于现有PyGram Python代码()为希望在浏览器和/或Node.js中使用PQ-Gram算法使用树编辑距离近似的人编写了一个实现()

jqgram树编辑距离近似模块为服务器端和浏览器端应用程序实现PQ Gram算法;O(n logn)时间和O(n)空间性能,其中n是节点数。参见算法来源的学术论文:()

PQ-Gram近似比通过Zhang&Shasha、Klein或Guha等人获得真实编辑距离要快得多,他们提供的真实编辑距离算法都执行最小O(n^2)时间,因此通常不适用

在实际应用中,如果可以获得多棵树与已知标准的相对近似值,则通常不需要知道真实编辑距离。随着Node.js的出现,浏览器和服务器上的Javascript频繁处理树结构,最终用户的性能通常是算法实现和设计的关键;因此jqgram

例如:

var jq = require("jqgram").jqgram;
var root1 = {
    "thelabel": "a",
    "thekids": [
        { "thelabel": "b",
        "thekids": [
            { "thelabel": "c" },
            { "thelabel": "d" }
        ]},
        { "thelabel": "e" },
        { "thelabel": "f" }
    ]
}

var root2 = {
    "name": "a",
    "kiddos": [
        { "name": "b",
        "kiddos": [
            { "name": "c" },
            { "name": "d" },
            { "name": "y" }
        ]},
        { "name": "e" },
        { "name": "x" }
    ]
}

jq.distance({
    root: root1,
    lfn: function(node){ return node.thelabel; },
    cfn: function(node){ return node.thekids; }
},{
    root: root2,
    lfn: function(node){ return node.name; },
    cfn: function(node){ return node.kiddos; }
},{ p:2, q:3, depth:10 },
function(result) {
    console.log(result.distance);
});
请注意,lfn和cfn参数指定了每个树应该如何独立地确定每个树根的节点标签名称和子数组,以便您可以执行一些有趣的操作,例如将对象与浏览器DOM进行比较。您所需要做的就是将这些函数与每个根一起提供,其余的由jqgram完成,调用lfn和cfn提供的函数来构建树。因此,从这个意义上讲,它(无论如何在我看来)比PyGram更容易使用。另外,它的Javascript,所以使用它的客户端或服务器端

现在可以使用的一种方法是使用jqgram或PyGram来获得几棵接近的树,然后对一组较小的树使用真正的编辑距离算法,为什么要将所有的计算都花费在已经可以轻松确定的树上,或者反之亦然。因此,您也可以使用jqgram缩小选择范围


希望代码能帮助一些人

我为算法制作了一个简单的python包装器(apted.py),如果有人感兴趣,可以使用:

# To use, create a folder named lib next to apted.py, then put APTED.jar into it

import os, os.path, jpype 

global distancePackage
distancePackage = None

global utilPackage
utilPackage = None

def StartJVM():
  # from http://www.gossamer-threads.com/lists/python/python/379020
  root = os.path.abspath(os.path.dirname(__file__)) 
  jpype.startJVM(jpype.getDefaultJVMPath(), 
  "-Djava.ext.dirs=%s%slib" % (root, os.sep))
  global distancePackage
  distancePackage = jpype.JPackage("distance")
  global utilPackage
  utilPackage = jpype.JPackage("util")


def StopJVM():
  jpype.shutdownJVM()


class APTED:
  def __init__(self, delCost, insCost, matchCost):
    global distancePackage
    if distancePackage is None:
      raise Exception("Need to call apted.StartJVM() first")
    self.myApted = distancePackage.APTED(float(delCost), float(insCost), float(matchCost))

  def nonNormalizedTreeDist(self, lblTreeA, lblTreeB):
    return self.myApted.nonNormalizedTreeDist(lblTreeA.myLblTree, lblTreeB.myLblTree)


class LblTree:
  def __init__(self, treeString):
    global utilPackage
    if utilPackage is None:
      raise Exception("Need to call apted.StartJVM() first")

    self.myLblTree = utilPackage.LblTree.fromString(treeString)

'''
# Example usage:

import apted
apted.StartJVM()
aptedDist = apted.APTED(delCost=1, insCost=1, matchCost=1)
treeA = apted.LblTree('{a}')
treeB = apted.LblTree('{b{c}}')
dist = aptedDist.nonNormalizedTreeDist(treeA, treeB)
print dist


# When you are done using apted
apted.StopJVM()
# For some reason it doesn't usually let me start it again and crashes python upon exit when I do this so call only as needed
'''

此处链接的实现不正确。(参见我的答案)我通过移植开始了我的实现,但当我最终找到它引用的论文时,我发现与原始论文有一些偏差,导致它无法通过对称性、三角形不等式等基本测试。感谢您的支持——很高兴您能够正确实现Zhang Shasha算法。很抱歉,我链接的代码不起作用。Steve的fork不再是该算法的规范fork请参见:以下是此java实现的.NET端口: