web-dev-qa-db-fra.com

Détecter les différences entre les structures arborescentes

Il s'agit plus d'une question CS, mais intéressante:

Disons que nous avons 2 structures arborescentes avec plus ou moins les mêmes nœuds réorganisés. Comment trouveriez-vous

  1. tout
  2. dans un certain sens minime

séquence d'opérations

  • MOVE(A, B) - déplace le nœud A sous le nœud B (avec tout le sous-arbre)
  • INSERT(N, B) - insère un nouvea nœud N sous le nœud B
  • DELETE (A) - supprime le nœud A (avec tout le sous-arbre)

qui transforme un arbre en un autre.

Il peut évidemment y avoir des cas où une telle transformation n'est pas possible, trivial étant racine A avec enfant B en racine B avec enfant A etc.). Dans de tels cas, l'algorithme fournirait simplement un résultat " impossible ".

Une version encore plus spectaculaire est une généralisation pour les réseaux, c'est-à-dire lorsque nous supposons qu'un nœud peut se produire plusieurs fois dans l'arbre (ayant effectivement plusieurs "parents"), alors que les cycles sont interdits.

Disclaimer: Ceci est pas un devoir, en fait cela vient d'un vrai problème commercial et j'ai trouvé assez intéressant de me demander si quelqu'un pourrait connaître une solution.

70
Tomas Vana

Il y a non seulement un article Wikipedia sur l'isomorphisme des graphes (comme le souligne Space_C0wb0y) mais aussi un article dédié sur le problème d'isomorphisme des graphes . Il a une section Solved special cases pour lesquels des solutions en temps polynomial sont connues. Trees en fait partie et cite les deux références suivantes:

21
Andre Holzner

Vous ne saviez pas si vous compariez des arbres de syntaxe abstraite pour le code source, des documents XML interprétés comme des arbres ou un autre type d'arbre.

Il existe un certain nombre d'articles qui discutent de la comparaison des arbres de syntaxe et du calcul des distances minimales par divers moyens. Les idées doivent être pertinentes.

Un bon article est Change Distilling , qui essaie de comparer le code source de deux arbres de syntaxe abstraite et de signaler une différence minimale. Le papier parle d'une méthode spécifique, et mentionne également brièvement (et fournit des références) à une variété de techniques similaires.

Peu de ces algorithmes sont réellement réalisés dans les outils disponibles pour comparer le texte source. Notre Smart Differencer est l'un d'entre eux.

14
Ira Baxter

Bien que cette question soit ancienne, j'ajouterai quelques références et algorithmes supplémentaires ci-dessous:

  1. X-Diff: un algorithme de détection de changement efficace pour les documents XML, Yuan Wang, David J. DeWitt, Jin-Yi Cai
  2. KF-Diff +: Algorithme de détection de changement hautement efficace pour les documents XML
  3. diffX: un algorithme pour détecter les changements dans les documents XML multi-versions
  4. Détection de changement dans les arbres XML: une enquête, Luuk Peters
  5. Similitude dans les structures de données arborescentes

De plus, il existe des bibliothèques et des cadres sur GitHub (en javascript) qui implémentent différentes structures arborescentes, par exemple des applications traitant des données JSON ou des arbres XML (par exemple pour MVC/MVVM côté client):

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

Dans le cas où les gens trouveraient cette question et auraient besoin de quelque chose d'implémenté pour Node.js ou le navigateur, je fournis un exemple de lien et de code pour une implémentation que j'ai écrite que vous pouvez trouver sur github ici: ( https://github.com/hoonto/jqgram.git ) basé sur le code PyGram existant Python ( https://github.com/Sycondaman/PyGram ) .

Il s'agit d'un algorithme d'approximation de la distance d'édition d'arbre , mais il est beaucoup, beaucoup plus rapide que d'essayer de trouver la vraie distance d'édition. L'approximation fonctionne en O (n log n) temps et O(n) espace alors que la vraie distance d'édition est souvent O (n ^ 3) ou O (n ^ 2) en utilisant des algorithmes connus pour vrai modifier la distance. Voir l'article académique d'où provient l'algorithme PQ-Gram: ( http://www.vldb2005.org/program/paper/wed/p301-augsten.pdf )

Donc, en utilisant jqgram:

Exemple:

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);
});

Et cela vous donne un nombre compris entre 0 et 1. Plus près de zéro, plus les deux arbres sont étroitement liés à jqgram. Une approche pourrait être d'utiliser jqgram pour restreindre plusieurs arbres étroitement liés parmi de nombreux arbres compte tenu de sa vitesse, puis utiliser la vraie distance d'édition sur les quelques arbres restants dont vous avez besoin pour examiner de plus près, et pour cela, vous pouvez trouver = python implémentations pour référence ou port de l'algorithme Zhang & Shasha par exemple.

Notez que les paramètres lfn et cfn spécifient comment chaque arbre doit déterminer les noms d'étiquette de nœud et le tableau enfants pour chaque racine d'arbre indépendamment afin que vous puissiez faire des choses amusantes comme comparer un objet à un DOM de navigateur par exemple. Tout ce que vous devez faire est de fournir ces fonctions avec chaque racine et jqgram fera le reste, en appelant les fonctions fournies par lfn et cfn pour construire les arbres. Donc, dans ce sens, il est (à mon avis en tout cas) beaucoup plus facile à utiliser que PyGram. De plus, son Javascript, alors utilisez-le côté client ou serveur!

ÉGALEMENT, pour répondre en ce qui concerne la détection de cycle, consultez la méthode de clonage à l'intérieur de jqgram, il y a une détection de cycle là-bas, mais cela revient à l'auteur du nœud-clone à partir duquel cette pièce a été légèrement modifiée et incluse.

8
hoonto