web-dev-qa-db-fra.com

Algorithme pour trouver le plus bas ancêtre commun dans un graphe acyclique dirigé?

Imaginez un graphe acyclique dirigé comme suit:

  • "A" est la racine (il y a toujours exactement une racine)
  • chaque nœud connaît son ou ses parents
  • les noms de nœuds sont arbitraires - rien ne peut en être déduit
  • nous savons par une autre source que les noeuds ont été ajoutés à l’arbre dans l’ordre A à G (par exemple, ils sont validés dans un système de contrôle de version)

Directed Acyclic Graph

Quel algorithme pourrais-je utiliser pour déterminer l'ancêtre commun le plus bas (ACL) de deux nœuds arbitraires, par exemple, l'ancêtre commun de:

  • B et E est B
  • D et F est B

Remarque:

25
Andrew Swan

Le lien de Den Roman semble prometteur, mais cela me semblait un peu compliqué, alors j'ai essayé une autre approche. Voici un algorithme simple que j'ai utilisé:

Supposons que vous vouliez calculer l'ACL (x, y) avec x et y deux nœuds . Chaque nœud doit avoir une valeur color et count, resp. initialisé à white et 0.

  1. Colorie tous les ancêtres de x comme blue (peut être fait avec BFS )
  2. Colorie tous les ancêtres blue de y comme red (encore une fois BFS)
  3. Pour chaque noeud red du graphique, incrémentez la variable count de ses parents d'une unité.

Chaque nœud red ayant une valeur count définie sur 0 est une solution.

Il peut y avoir plus d'une solution, selon votre graphique. Par exemple, considérons ce graphique:

 DAG example where LCA can have two values

LCA (4,5) solutions possibles sont 1 et 2.

Notez que cela fonctionne toujours si vous voulez trouver l’ACV de 3 nœuds ou plus, il vous suffit d’ajouter une couleur différente pour chacun d’eux.

8

Je cherchais une solution au même problème et j'ai trouvé une solution dans l'article suivant:

http://dx.doi.org/10.1016/j.ipl.2010.02.014

En bref, vous ne recherchez pas l’ancêtre commun le plus bas, mais l’ancêtre commun UNIQUE le plus bas, qu’ils définissent dans le présent document. 

5
Steven Obua

Juste une pensée sauvage. Pourquoi ne pas utiliser les deux nœuds d’entrée en tant que racines et exécuter deux BFS simultanément, étape par étape? À une certaine étape, lorsque leurs ensembles BLACK se chevauchent (enregistrement des nœuds visités), l'algorithme s'arrête et les nœuds qui se chevauchent sont leur (s) ACL. De cette manière, tous les ancêtres communs auront des distances plus longues que celles que nous avons découvertes.

1
Ning

Je sais que c'est une vieille question et une assez bonne discussion, mais comme j'avais un problème similaire à résoudre, je suis tombé sur JGraphT 's Plus bas ancêtre commun algorithmes, j'ai pensé que cela pourrait aider:

1
Hamid Nazari

Si le graphique a des cycles, alors «ancêtre» est défini de manière vague. Peut-être que vous voulez parler de l'ancêtre sur la sortie de l'arborescence d'un DFS ou d'un BFS? Ou peut-être par «ancêtre», vous voulez dire le nœud du digraphe qui minimise le nombre de sauts de E et B

Si vous n'êtes pas inquiet pour la complexité, vous pouvez alors calculer un A * (ou le plus court chemin de Dijkstra) de chaque nœud à la fois pour E et B. Pour les nœuds pouvant atteindre E et B, vous pouvez trouver le nœud qui minimise PathLengthToE + PathLengthToB.

EDIT: Maintenant que vous avez clarifié quelques points, je pense que je comprends ce que vous recherchez.

Si vous ne pouvez que "monter" dans l’arbre, alors je vous suggère d’effectuer un BFS à partir de E et également un BFS à partir de B. Chaque variable de votre graphique est associée à deux variables: les sauts de B et les sauts de E. Laissez B et E des copies de la liste des noeuds de graphe. La liste de B est triée par Hops from B tandis que la liste de E est triée par Hops from E

Pour chaque élément de la liste B, essayez de le trouver dans la liste E. Placez les correspondances dans une troisième liste, triées par nombre de sauts de B + Houblons de E. Une fois que vous avez épuisé la liste de B, votre troisième liste triée doit contenir la LCA en tête. Cela permet une solution, plusieurs solutions (choisies arbitrairement parmi leur ordre de BFS pour B) ou aucune solution.

0
AndyG

http://www.gghh.name/dibtp/2014/02/25/how-does-Mercurial-select-the-greatest-common-ancestor.html

Ce lien décrit la procédure à suivre dans Mercurial. L’idée de base est de rechercher tous les parents pour les nœuds spécifiés, de les regrouper par distance à partir de la racine, puis de rechercher ces groupes.

0
Den Roman

Supposons que vous souhaitiez trouver les ancêtres de x et y dans un graphique.

Maintenir un tableau de vecteurs - parents (stockage des parents de chaque noeud).

  1. Tout d'abord, faites un bfs (gardez les parents de chaque sommet) et trouvez tous les ancêtres de x (trouvez les parents de x et utilisez parents , trouvez tous les ancêtres de x) et stockez-les dans un vecteur. Enregistrez également la profondeur de chaque parent dans le vecteur.

  2. Trouvez les ancêtres de y en utilisant la même méthode et stockez-les dans un autre vecteur. Maintenant, vous avez deux vecteurs stockant les ancêtres de x et y respectivement avec leur profondeur.

  3. ACV serait un ancêtre commun avec la plus grande profondeur. La profondeur est définie comme la plus longue distance de la racine (sommet avec in_degree = 0). Nous pouvons maintenant trier les vecteurs par ordre décroissant de profondeur et déterminer l’ACV. En utilisant cette méthode, nous pouvons même trouver plusieurs ACV (le cas échéant).

0
shiwang

J'ai également besoin de la même chose, trouver l'ACL dans un DAG (graphe acyclique dirigé). Le problème LCA est lié à RMQ (Range Minimum Query Problem). 

Il est possible de réduire l'ACV à RMQ et de trouver l'ACV souhaitée de deux nœuds arbitraires à partir d'un graphe acyclique dirigé. 

J'ai trouvé CE TUTORIAL détail et bien. Je prévois également de mettre cela en œuvre.

0
Forhad

Je propose la solution de complexité temporelle O (| V | + | E |), et je pense que cette approche est correcte, sinon corrigez-moi.

Étant donné le graphe acyclique dirigé, nous devons trouver une ACV de deux sommets v et w.

Étape 1: recherchez la distance la plus courte entre tous les sommets du sommet racine à l’aide de bfs http://en.wikipedia.org/wiki/Breadth-first_search avec une complexité temporelle O (| V | + | E |) et recherchez également le parent de chaque sommets.

Étape 2: Recherchez les ancêtres communs des deux sommets en utilisant le parent jusqu'à atteindre le sommet de la racine. Complexité temporelle- 2 | v |

Étape 3: L'ACV sera cet ancêtre commun qui aura la distance maximale la plus courte.

Donc, c’est l’algorithme de complexité temporelle O (| V | + | E |).

S'il vous plaît, corrigez-moi si je me trompe ou si d'autres suggestions sont les bienvenues. 

0
Nitish Jain