web-dev-qa-db-ja.com

ツリー構造間の違いを検出する

これはCSの質問ですが、興味深い質問です。

ほぼ同じノードが再編成された2つのツリー構造があるとします。どうやって見つけますか

  1. any
  2. ある意味でminimal

操作のシーケンス

  • MOVE(A, B)-ノードAをノードBの下に移動します(サブツリー全体を含む)
  • INSERT(N, B)-ノードBの下にnewノードNを挿入します
  • DELETE (A)-ノードAを(サブツリー全体で)削除します

1つのツリーを別のツリーに変換します。

明らかに、このような変換が不可能な場合があります。子Aを含むルートBから子Bを含むルートAなどの些細なことです。そのような場合、アルゴリズムは単に結果「notnotable」を配信します。

さらに壮観なバージョンは、ネットワークの一般化です。つまり、ノードがツリー内で複数回発生する可能性があると仮定すると(事実上、複数の「親」を持つ)、サイクルは禁止されます。

免責事項:これはnot宿題であり、実際には実際のビジネス上の問題に由来するものであり、誰かが解決策を知っているかどうか疑問に思って非常に興味深いことがわかりました。

70
Tomas Vana

グラフ同型に関するウィキペディアの記事(Space_C0wb0yが指摘しているように)だけでなく、 グラフ同型問題 に関する専用記事もあります。多項式時間解が知られているセクションSolved special casesがあります。 Treesはそれらの1つであり、次の2つの参照を引用しています。

21
Andre Holzner

ソースコードの抽象構文ツリー、ツリーとして解釈されるXMLドキュメント、または他の種類のツリーを比較するかどうかは明確ではありませんでした。

構文木を比較し、さまざまな手段で最小距離を計算することを議論する多くの論文があります。アイデアは関連する必要があります。

良い論文は Change Distilling です。これは、2つの抽象的な構文ツリーのソースコードを比較し、最小限の違いを報告しようとします。このペーパーでは、特定の方法について説明しています。また、さまざまな同様の手法についても簡潔に言及しています(そして参照も提供しています)。

これらのアルゴリズムのほとんどは、ソーステキストを比較するための利用可能なツールで実際に実現されています。 Smart Differencer はその1つです。

14
Ira Baxter

この質問は古いものですが、さらにいくつかの参照とアルゴリズムを以下に追加します。

  1. X-Diff:XMLドキュメントの効果的な変更検出アルゴリズム、Yuan Wang、David J.DeWitt、Jin-Yi Cai
  2. KF-Diff +:XMLドキュメントの高効率変更検出アルゴリズム
  3. diffX:複数バージョンのXMLドキュメントの変更を検出するアルゴリズム
  4. XMLツリーの変更検出:調査、Luuk Peters
  5. ツリーデータ構造の類似性

さらに、GitHubには、JSONデータまたはXMLツリーを処理するアプリケーションの例(クライアント側のMVC/MVVM用)のツリーのような構造の差分を実装するライブラリとフレームワークがあります:

  1. React.js
  2. JSON-Patch
  3. jsondiffpatch
  4. objectDiff
11
Nikos M.

人々がこの質問を見つけ、Node.jsまたはブラウザ用に実装されたものが必要な場合、ここでgithubで見つけることができる私が書いた実装のリンクとコード例を提供しています:( https:/ /github.com/hoonto/jqgram.git )既存のPyGramに基づくPython code( https://github.com/Sycondaman/PyGram ) 。

これはツリー編集距離近似アルゴリズムですが、実際の編集距離を見つけるよりもはるかに高速です。近似は、O(n log n)時間とO(n) space距離を編集します。PQ-Gramアルゴリズムの由来となる学術論文を参照してください:( http://www.vldb2005.org/program/paper/wed/p301-augsten.pdf

したがって、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 },
function(result) {
    console.log(result.distance);
});

そして、0から1の間の数値が得られます。ゼロに近づくほど、2つのツリーはjqgramに密接に関連します。 1つのアプローチは、jqgramを使用して、速度を考慮して多数のツリーの中から密接に関連するいくつかのツリーを絞り込み、残りのいくつかのツリーの真の編集距離を利用して、詳細な検査を行う必要があります。 python Zhang&Shashaアルゴリズムの参照またはポートの実装。

Lfnパラメータとcfnパラメータは、各ツリーがノードラベル名と各ツリールートの子配列を個別に決定する方法を指定するため、たとえばオブジェクトをブラウザDOMと比較するなどのファンキーなことができることに注意してください。必要なのは、これらの関数を各ルートとともに提供することだけです。jqgramが残りを行い、lfnおよびcfnが提供する関数を呼び出してツリーを構築します。したがって、その意味では(とにかく私の意見では)PyGramよりもはるかに使いやすいです。さらに、Javascriptなので、クライアント側またはサーバー側で使用してください!

また、サイクル検出に関して答えるには、jqgram内のcloneメソッドをチェックしてください。そこにはサイクル検出がありますが、その功績は、その部分がわずかに変更されて含まれているノードクローンの作者にあります。

8
hoonto