2009-06-30 173 views
22

我需要爲我的個人項目計算樹之間的編輯距離。論文描述了一種算法,但是我無法從中找出頭或尾。您是否知道以更易於理解的方式描述適用算法的資源?僞代碼或代碼也會有幫助。如何計算樹編輯距離?

回答

8

下面是一些java source code(gzip壓縮包,位於底部),用於樹編輯距離算法,可能對您有用。

該頁面包含參考文獻和一些幻燈片,通過「張和莎莎」算法一步一步和其他有用的鏈接,讓您瞭解速度。

編輯︰雖然這個答案被接受,因爲它指向張沙沙算法,鏈接中的代碼有錯誤。史蒂夫約翰遜和tim.tadh提供了working Python code。有關更多詳細信息,請參閱Steve Johnson's comment

+1

這裏鏈接的實現是不正確的。 (請參閱我的答案。)我通過移植它開始實施,但是當我最終找到它所參考的論文時,發現原始論文的一些偏離導致其無法對稱,三角不等的基本測試等。 – 2010-11-24 17:32:04

-6

我想你只是尋找兩個頂點之間的最短路徑。如果是這樣,你可以使用Dijkstra's algorithm。 (維基百科頁面上有僞代碼供您參考。)

+0

樹編輯距離是與「編輯腳本」相關的成本(系列的編輯操作)將一棵樹變成另一棵。我不認爲你可以直接使用Dijkstra的算法。 – Naaff 2009-06-30 19:21:26

+2

@Naaff:其實你可以使用Dijkstra的算法(我不會推薦它)。圖的節點將是OP的問題的樹,並且它們將具有編輯距離爲1的樹的邊緣。此圖是無限的,因此您不會將其存儲在內存中,而是會在您隨時計算它。對於不太接近這個算法的樹來說,它的性能和內存消耗會非常糟糕。 – yairchu 2009-07-01 10:11:00

+0

@yairchu:謝謝你的見解。有趣的是,如果一個人能*使用Dijkstra的算法,即使一個人不應該*。 :) – Naaff 2009-07-01 20:38:45

21

(編輯這個答案鏈接到當前版本的執行由tim.tadh給出)

這個Python庫實現了章殺殺算法正確https://github.com/timtadh/zhang-shasha

它開始作爲一個直接端口在當前接受的答案(使用tarball鏈接的答案)中列出的Java源代碼,但該實現是不正確,幾乎不可能運行。

+0

感謝您的回饋 - 很高興您能夠正確實施Zhang-Shasha算法。抱歉,我鏈接的代碼無法正常工作。 – Naaff 2010-11-24 18:27:26

2

這裏你可以找到的樹編輯距離算法的Java實現:

http://www.inf.unibz.it/dis/projects/tree-edit-distance

除了1989張&傻傻的算法,也有比較近的算法,包括1998年克萊恩,Demaine樹編輯距離實現等人。 2009年,由Pawlik & Augsten樂百氏樹編輯距離(RTED)算法,2011年

5

我寫了一個基於現有PyGram Python代碼(https://github.com/Sycondaman/PyGram)對於那些你誰希望使用樹編輯的實現(https://github.com/hoonto/jqgram.git)在瀏覽器和/或Node.js中使用PQ-Gram算法進行距離近似。

jqgram樹編輯距離近似模塊實現了服務器端和瀏覽器端應用程序的PQ-Gram算法; O(n log n)時間和O(n)空間性能,其中n是節點的數量。請參閱算法出現的學術論文:(http://www.vldb2005.org/program/paper/wed/p301-augsten.pdf

PQ-Gram近似比通過Shasha,Klein或Guha等人獲得真正的編輯距離要快得多。 al,他們提供了真正的編輯距離算法,它們都執行最小O(n^2)時間,因此通常是不合適的。

通常在實際應用中,如果可以獲得多棵樹相對於已知標準的相對近似值,則不需要知道真正的編輯距離。 JavaScript,在瀏覽器中,隨着Node.js的出現,現在在服務器上經常處理樹結構,最終用戶的性能在算法實現和設計中通常是至關重要的;因此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來縮小選擇的範圍。

希望代碼可以幫助一些人。

1

我做了一個簡單的Python包裝(apted.py)使用jpype如果有人有興趣的APTED算法:

# 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 
'''