132 votes

Poids négatifs à l'aide de l'algorithme de Dijkstra

J'essaie de comprendre pourquoi l'algorithme de Dijkstra ne fonctionne pas avec des poids négatifs. En lisant un exemple sur Les chemins les plus courts J'essaie de comprendre le scénario suivant :

    2
A-------B
 \     /
3 \   / -2
   \ /
    C

Extrait du site web :

En supposant que les arêtes sont toutes dirigées de gauche à droite. avec A, l'algorithme de Dijkstra choisira l'arête (A,x) minimisant d(A,A)+longueur(bord), à savoir (A,B). Il définit ensuite d(A,B)=2 et choisit un autre bord (y,C) minimisant d(A,y)+d(y,C) ; le seul choix est (A,C). et il fixe d(A,C)=3. Mais elle ne trouve jamais le plus court chemin de A à B, via C, avec une longueur totale de 1.

Je ne comprends pas pourquoi, en utilisant l'implémentation suivante de Dijkstra, d[B] ne sera pas mis à jour en 1 (Lorsque l'algorithme atteint le sommet C, il exécute un relax sur B, voit que le d[B] est égal à 2 et donc de mettre à jour sa valeur à 1 ).

Dijkstra(G, w, s)  {
   Initialize-Single-Source(G, s)
   S  Ø
   Q  V[G]//priority queue by d[v]
   while Q  Ø do
      u  Extract-Min(Q)
      S  S U {u}
      for each vertex v in Adj[u] do
         Relax(u, v)
}

Initialize-Single-Source(G, s) {
   for each vertex v  V(G)
      d[v]  
      [v]  NIL
   d[s]  0
}

Relax(u, v) {
   //update only if we found a strictly shortest path
   if d[v] > d[u] + w(u,v) 
      d[v]  d[u] + w(u,v)
      [v]  u
      Update(Q, v)
}

Gracias,

Meir

0 votes

La recherche de chemin en général avec des poids de bord négatifs est extrêmement difficile. Quel que soit le chemin que vous trouvez, il y a toujours la possibilité d'un chemin arbitrairement long avec un poids négatif arbitrairement grand quelque part le long du chemin. Je ne serais pas surpris qu'il s'agisse d'un NP complet.

4 votes

Pour tous ceux qui ont ce doute, vous pouvez trouver le chemin le plus court dans un graphe DONNÉ qu'il n'a pas de cycles de poids négatifs. L'algorithme ci-dessus fonctionnerait si la fonction Relax renvoyait une valeur "true" lorsque la relaxation est effectivement réussie, auquel cas, le sommet adjacent "v" serait mis en file d'attente dans la file prioritaire s'il n'est pas présent, ou mis à jour s'il est déjà présent. Cela signifie que les nœuds visités peuvent à nouveau être ajoutés à la file d'attente prioritaire au fur et à mesure qu'ils sont relaxés.

241voto

templatetypedef Points 129554

L'algorithme que vous avez suggéré trouvera effectivement le plus court chemin dans ce graphe, mais pas tous les graphes en général. Par exemple, considérons ce graphe :

A directed graph with four nodes, A, B, C, and D. Node A has an edge to B of cost 1, an edge to C of cost 0, and an edge to D of cost 99. Node B has an edge to cost 1 to node C. Node D has an edge of cost -300 to node B.

Retraçons l'exécution de votre algorithme.

  1. Tout d'abord, vous définissez d( A ) à 0 et les autres distances à .
  2. Vous élargissez ensuite le nœud A , en fixant d( B ) à 1, d( C ) à 0, et d( D ) à 99.
  3. Ensuite, vous élargissez C sans aucun changement net.
  4. Vous vous étendez ensuite B qui n'a aucun effet.
  5. Enfin, vous élargissez D ce qui modifie d( B ) à -201.

Remarquez qu'à la fin, cependant, que d( C ) est toujours égal à 0, même si le chemin le plus court vers C a une longueur de -200. Cela signifie que votre algorithme ne calcule pas les distances correctes à tous les nœuds. De plus, même si vous stockiez des pointeurs indiquant comment aller de chaque nœud au nœud de départ A vous finirez par prendre le mauvais chemin pour revenir de C a A .

La raison en est que l'algorithme de Dijkstra (et votre algorithme) sont algorithmes avides qui partent du principe qu'une fois qu'ils ont calculé la distance d'un nœud, la distance trouvée doit être la distance optimale. En d'autres termes, l'algorithme ne se permet pas de prendre la distance d'un nœud qu'il a développé et de modifier cette distance. Dans le cas d'arêtes négatives, votre algorithme, et l'algorithme de Dijkstra, peuvent être "surpris" en voyant une arête à coût négatif qui diminuerait effectivement le coût du meilleur chemin du nœud de départ à un autre nœud.

J'espère que cela vous aidera !

43 votes

Pour ajouter à votre excellente réponse : Dijkstra étant un algorithme de gourmandise est la raison de son choix à courte vue.

6 votes

Je voudrais souligner que, techniquement, tous les chemins de ce graphique ont un coût de l'infini négatif grâce au cycle négatif A,D,B,A.

5 votes

@Nate- Pour clarifier, toutes les arêtes du graphe sont dirigées de gauche à droite. C'était un peu difficile de rendre les flèches dans mon art ASCII de haute qualité. :-)

39voto

infty10000101 Points 11

Notez que Dijkstra fonctionne même pour des poids négatifs, si le graphe n'a pas de cycles négatifs, c'est-à-dire des cycles dont la somme des poids est inférieure à zéro.

Bien sûr, on peut se demander pourquoi, dans l'exemple donné par templatetypedef, Dijkstra échoue alors qu'il n'y a pas de cycles négatifs, en fait pas même de cycles. C'est parce qu'il utilise un autre critère d'arrêt, qui arrête l'algorithme dès que le nœud cible est atteint (ou que tous les nœuds ont été réglés une fois, il ne l'a pas précisé exactement). Dans un graphe sans poids négatifs, cela fonctionne bien.

Si l'on utilise le critère d'arrêt alternatif, qui arrête l'algorithme lorsque la file d'attente des priorités (tas) est vide (ce critère d'arrêt a également été utilisé dans la question), alors dijkstra trouvera la distance correcte même pour les graphes avec des poids négatifs mais sans cycles négatifs.

Cependant, dans ce cas, la limite de temps asymptotique de dijkstra pour les graphes sans cycles négatifs est perdue. Cela est dû au fait qu'un nœud précédemment réglé peut être réinséré dans le tas lorsqu'une meilleure distance est trouvée en raison des poids négatifs. Cette propriété est appelée correction d'étiquette.

0 votes

2. La raison pour laquelle vous pensez que le temps serait "plus proche de Bellman-Ford" et non exponentiel (ce qui est pire que Bellman-Ford) n'est pas claire. Avez-vous un algorithme concret et une preuve à l'esprit ?

3 votes

En réponse à la question 1 : comme vous pouvez utiliser exactement la même implémentation de dijkstra avec le critère d'arrêt mentionné, qui s'arrête lorsque la file d'attente est vide (voir le pseudocode dans la question originale), il s'agit toujours de l'algorithme de dijkstra pour les plus courts chemins, même s'il se comporte différemment en réglant les nœuds plusieurs fois (correction des étiquettes).

1 votes

Pour 2. : C'était juste une supposition donc je vais l'effacer. Je pense que vous avez raison pour le temps exponentiel, car il y a un nombre exponentiel de chemins à explorer.

23voto

Kerrek SB Points 194696

Je comprends la question de l'OP comme suit pourquoi L'algorithme de Dijkstra échoue dans ce cas. Voyons voir.

Tout d'abord, la déclaration correcte est que "l'algorithme de Dijkstra échoue s'il y a des négatifs dirigé bords". S'il y a des non orienté alors chaque bord négatif constitue immédiatement un bord négatif. cycle ( X->Y->X ), et le graphique ne ont un chemin le plus court et la question est discutable.

Pour que cela ne soit pas trivial, supposons que le graphe soit orienté :

A -> B : 2
A -> C : 3
C -> B : -2

Supposons encore que notre source soit A . Le vrai plus court chemin de A à B est A-C-B avec un poids de 1. Mais l'algorithme de Dijkstra commence par sélectionner A->B et supprime ensuite cet avantage de toute considération future. parce qu'elle suppose qu'elle ne peut pas être améliorée .

Ceci est crucial : Sous l'hypothèse de non-négativité, nous pouvons être sûrs une fois pour toutes que le plus court chemin de A à B est A -> B : 2 . Comme l'algorithme suppose cela, il ne vérifie jamais plus tard s'il existe un chemin encore plus court, car avec des arêtes non négatives, il ne peut pas y en avoir.

Cette hypothèse permet à l'algorithme de procéder avec moins d'étapes, mais elle l'empêche d'identifier correctement le plus court chemin en présence d'arêtes négatives. Les bords négatifs rendent le problème "non local" dans le sens où vous ne pouvez pas savoir si vous vous en sortez bien en regardant simplement vos voisins immédiats - il pourrait toujours y avoir un bord extrêmement négatif juste après ce bord très cher à côté de vous.

11voto

amit Points 74385

Vous n'avez utilisé S nulle part dans votre algorithme (à part le modifier). l'idée de dijkstra est qu'une fois qu'un sommet est sur S, il ne sera plus jamais modifié. dans ce cas, une fois que B est dans S, vous ne l'atteindrez plus via C.

ce fait garantit une complexité de O(E+VlogV) [sinon, vous répéterez les arêtes plus d'une fois, et les sommets plus d'une fois].

en d'autres termes, l'algorithme que vous avez posté, pourrait ne pas être en O(E+VlogV), comme promis par l'algorithme de dijkstra.

0 votes

En outre, il n'est pas nécessaire de modifier le sommet sans arêtes de poids négatif, ce qui rompt complètement l'hypothèse selon laquelle les coûts des chemins ne peuvent augmenter qu'avec des arêtes répétées.

0 votes

Cette hypothèse est exactement ce qui nous permet d'utiliser S, et de "savoir" qu'une fois qu'un sommet est dans S, il ne sera plus jamais modifié.

1 votes

Votre dernière affirmation est fausse. L'algorithme affiché a une complexité temporelle O(E + VlogV) lorsqu'il travaille sur des graphes sans arêtes négatives. Il n'est pas nécessaire de vérifier qu'on a déjà visité un noeud, puisque le fait qu'il ait été visité garantit que la procédure de relaxation ne l'ajoutera pas une fois de plus dans la file d'attente.

1voto

prusswan Points 4055

Considérez ce qui se passe si vous faites des allers-retours entre B et C... voilà !

(pertinent uniquement si le graphe n'est pas dirigé)

Modifié : Je crois que le problème est lié au fait que le chemin avec AC* ne peut être meilleur que AB qu'avec l'existence d'arêtes de poids négatif, donc peu importe où vous allez après AC, avec l'hypothèse d'arêtes de poids non négatif il est impossible de trouver un chemin meilleur que AB une fois que vous avez choisi d'atteindre B après avoir fait AC.

0 votes

Ce n'est pas possible, le graphe est dirigé.

0 votes

@amit : bon point, ça m'a échappé. Il est temps de reconsidérer le problème

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X