Nous avons présenté la méthode dijkstra et la méthode bellman-ford lors d'une session d'étude interne. À cette époque, je recherchais diverses preuves de l'algorithme, mais j'ai remarqué qu'il n'y avait pas beaucoup d'articles expliqués. Personnellement, je pense que la question «Pourquoi cela se produit-il?» Est un facteur important, je voudrais donc présenter ces algorithmes en gardant cela à l'esprit.
Historique de programmation: 1 an (Python uniquement) Thé Atcoder Je travaille pour une société de consignation d'analyse de données depuis avril 2020, mais pour une raison quelconque, j'étudie simplement les algorithmes.
Il est utilisé pour résoudre le problème de chemin le plus court du point de départ unique d'un graphe pondéré.
Pour chaque terme, Pondéré: coût nécessaire pour passer par le côté Point de départ unique: un point de départ Itinéraire le plus court: l'itinéraire du point de départ à la destination au moindre coût est.
Prenant le graphique ci-dessous comme exemple
Le but est de trouver l'itinéraire le plus court à partir du point de départ $ A $ vers tous les côtés.
À propos, l'itinéraire le plus court de $ A à E $ est
La route partielle de la route la plus courte est la route la plus courte partout.
Preuve
Temporairement, dans le graphique ci-dessus, l'itinéraire le plus court de $ A à E $
Parmi ceux-ci, $ (A → B → C coût: 3) $
Est une route partielle de cette route la plus courte, mais si vous utilisez le sommet B'pour cette section
Vous pouvez déplacer le coût de un de moins.
Ensuite, l'itinéraire le plus court de cette section est
Si vous trouvez le chemin le plus court dans l'ordre à partir du sommet lié au point de départ, vous trouverez enfin tous les chemins les plus courts. En effet, après tout, le chemin le plus court obtenu progressivement à partir du point de départ devient un chemin partiel du sommet qui dépasse ce point.
Trouvez le chemin le plus court entre tous les sommets. La principale différence avec la méthode Dyxtra est que ** les poids négatifs sont autorisés **.
Le processus de recherche de l'itinéraire le plus court est
Dans la figure ci-dessous,
À l'étape 1, vous pouvez commencer $ A $ et atteindre $ C $ au coût de 5 $, alors notez-le comme $ C: 5 $. Ensuite, $ A → B $ peut être atteint à $ -1 $, donc $ B: -1 $ et $ B → C $ peuvent être atteints au coût de -1 $ + 2 = 1 $. À ce stade, j'ai trouvé que je pouvais atteindre C à un coût inférieur via $ B $, donc je le mettrai à jour en $ C: 1 $. Faites cela pour tous les côtés. C'est la première fois. Après cela, si vous répétez (nombre de sommets-1) fois, vous pouvez partir de $ A $ et trouver le chemin le plus court vers tous les sommets.
Le nombre maximum de sommets qui passent du point de départ à la destination correspond aux sommets à l'exclusion du point de départ, c'est-à-dire (nombre de sommets-1). Dans le processus décrit ci-dessus, le chemin le plus court (le sommet qui peut être déplacé du point de départ dans la première boucle) est toujours trouvé. Dans la deuxième boucle, le chemin le plus court (le sommet qui peut être déplacé de l'itinéraire déterminé la première fois) est ** toujours ** trouvé. ・ ・ ・ Après tout, cette boucle consiste à trouver l'itinéraire le plus court dans l'ordre à partir du point de départ, il est donc possible de trouver l'itinéraire le plus court par itinéraire de relaxation.
Voir la figure ci-dessous. La partie encerclée est un cycle, mais si vous vous déplacez ici, le coût diminuera de -2. En d'autres termes, si vous utilisez ce cycle, vous pouvez réduire le coût à l'infini. C'est ce qu'on appelle la ** fermeture négative **.
La méthode de Bellmanford peut également détecter les routes fermées négatives. Plus tôt, j'ai confirmé que le chemin le plus court a été trouvé dans une boucle de (nombre de sommets-1) fois, mais le coût de tous les sommets est à nouveau calculé, et s'il existe un sommet qui peut mettre à jour le coût, il y a un chemin fermé négatif. Sera.
Il prend en charge l'entrée du format de liste de contiguïté (il existe également une matrice de contiguïté souvent utilisée chez les professionnels de la compétition) et détecte l'itinéraire le plus court par la méthode de Bellmanford. Voici un exemple d'entrée avec une fermeture négative sous le code (graphique dans la figure ci-dessus). Remplace le coût des sommets via la fermeture négative par $ -inf $.
<détails> Le montant du calcul est $ O (V ^ 2 + E) $ (nombre de sommets: $ V $, nombre de côtés: $ E $). Il peut être utilisé dans les graphiques avec des poids non négatifs. Le processus de recherche de l'itinéraire le plus court est la [vidéo de Yobinori](![Image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411380/a8c60f68-abfc- d92b-02bb-b915e8b8da19.png)
) Est si facile à comprendre qu'il meurt.
(La portée défensive de Yobinori est trop large ...) Je vais l'expliquer ici aussi.
Soit le coût du sommet (valeur initiale: $ S = 0 $, autre $ = ∞ $). Commencez par le sommet le moins coûteux. Initialement, $ S $ est le point de départ. Déterminez le coût du point de départ (vert sur la figure). Le coût de ce sommet ne sera plus mis à jour. Notez le coût du point de départ + les pondérations latérales sur les sommets qui peuvent être déplacés à partir du point de départ. Noté 7, 3, 1 $ en haut de la rangée du milieu. Le coût est incertain car il peut encore être mis à jour. Répétez les étapes 1 à 3 jusqu'à ce que tous les sommets soient confirmés. Parmi les sommets notés, celui avec le coût de 1 est le plus petit, alors confirmez-le et notez les sommets qui peuvent être déplacés à partir de ce sommet. Si vous pouvez déménager à moindre coût, mettez à jour le coût. ** Celui dont le coût est le plus bas parmi les sommets indéterminés ne peut pas être mis à jour à un coût inférieur **.
Comme il n'y a que des pondérations non négatives, même si vous empruntez un autre itinéraire, le coût sera de 0 ou plus. Par conséquent, parmi les indéterminés, le coût le plus petit peut être déterminé comme l'itinéraire le plus court. <détails> ・ Introduction à l'algorithme 3e édition Volume 2: Méthode avancée de conception et d'analyse ・ Structure de données avancée ・ Algorithme graphique (manuel du MIT standard mondial)
(T. Colmen, R. Rivest, C. Stein, C. Riserson, Tetsuo Asano, Kazuo Iwano, Hiroshi Umeo, Masashi Yamashita, Koichi Wada)
C'est un livre assez lourd, mais la preuve de la théorie des graphes était très polie. ・ Théorie des graphes ⑤ (algorithme de Dixtra)
("Maths et physique universitaires" appris à l'école préparatoire)
https://www.youtube.com/watch?v=X1AsMlJdiok
Vous pouvez pleinement comprendre la méthode Dyxtra simplement en regardant cette vidéo! ・ Programmation de compétition Arimoto Python Single Route Méthode 2 (Méthode Dyxtra)
(Le blog et le code de Juppy sont ici.)
https://juppy.hatenablog.com/entry/2018/11/01/%E8%9F%BB%E6%9C%AC_python_%E5%8D%98%E4%B8%80%E6%9C%80%E7%9F%AD%E7%B5%8C%E8%B7%AF%E6%B3%952%EF%BC%88%E3%83%80%E3%82%A4%E3%82%AF%E3%82%B9%E3%83%88%E3%83%A9%E6%B3%95%EF%BC%89_%E7%AB%B6%E6%8A%80%E3%83%97 Le graphique de l'article a été dessiné avec networkx.
Je vais mettre le code pour ceux qui s'en soucient. <détails>
Recommended Posts
def shortest_path(s,n,es):
#Distance la plus courte de s → i
# s:point de départ, n:Nombre de sommets, es[i]: [辺のpoint de départ,Fin de côté,Coût secondaire]
#Initialisation du poids. Notez la distance la plus courte ici
d = [float("inf")] * n
#Le point de départ est 0
d[s] = 0
#Calculez l'itinéraire le plus court
for _ in range(n-1):
# es:À propos du côté i[from,to,cost]
for p,q,r in es:
#Mis à jour si la racine de la flèche a été recherchée et qu'il existe un itinéraire coûtable
if d[p] != float("inf") and d[q] > d[p] + r:
d[q] = d[p] + r
#Vérifier route fermée négative
for _ in range(n-1):
# e:À propos du côté i[from,to,cost]
for p,q,r in es:
#Parce que le point à mettre à jour est affecté par la fermeture négative-mettre inf
if d[q] > d[p] + r:
d[q] = -float("inf")
return d
################
#contribution
n,w = map(int,input().split()) #n:Nombre de sommets w:Nombre de côtés
es = [] #es[i]: [Le point de départ du côté,Fin de côté,Coût secondaire]
for _ in range(w):
x,y,z = map(int,input().split())
es.append([x,y,z])
# es.append([y,x,z])
print(shortest_path(0,n,es))
'''
contribution
7 12
0 1 2
0 2 -5
0 3 5
1 2 3
1 4 1
1 5 3
2 4 6
3 2 -2
3 5 4
4 6 -5
5 4 -2
6 2 -3
'''
>>[0, 2, -inf, 5, -inf, 5, -inf]
Quelle est la méthode Dyxtra?
Pourquoi le trajet le plus court est-il nécessaire?
Alors, quel est le code source?
import heapq
def dijkstra(s, n, es):
#Distance la plus courte entre le point de départ s et chaque sommet
#n:Nombre de sommets, cost[u][v] :Coût UV latéral(Inf quand il n'existe pas)
d = [float("inf")] * n
#Liste de recherche
S = [s]
#Les files d'attente prioritaires réduisent la quantité de calcul
heapq.heapify(S)
d[s] = 0
while S:
#Extraire le pic du coût le plus bas
v = heapq.heappop(S)
# print("-----------v", v)
for u, w in es[v]:
# print(u, w)
#Mettre à jour si accessible à moindre coût et ajouter à la liste
if d[u] > d[v]+w:
d[u] = d[v]+w
heapq.heappush(S, u)
# print("d", d)
return d
n,w = map(int,input().split()) #n:Nombre de sommets w:Nombre de côtés
cost = [[float("inf") for i in range(n)] for i in range(n)]
es = [[] for _ in range(n)]
for i in range(w):
x, y, z = map(int, input().split())
es[x].append([y, z])
es[y].append([x, z]) #Supprimer cette ligne pour les graphiques dirigés
# print(es)
print(dijkstra(0, n, es))
Références ou sites
prime
#Graphique de la méthode Dikstra
import matplotlib.pyplot as plt
import networkx as nx
#Ensemble de graphiques dirigés
G = nx.DiGraph()
#Ajouter des arêtes
# G.add_edges_from([("A", "B", {"weight":2)])
# nx.add_path(G, ["A", "C"])
# G.edges["A", "B"]["weight"] = 2
G.add_weighted_edges_from([("A", "B", 5), ("A", "C", 7), ("A", "D", 1), ("B", "C", 3), ("B", "F", 3), ("D", "B", 2)])
G.add_weighted_edges_from([("C", "E", 3), ("D", "F", 8), ("F", "E", 2), ("B", "E", 6)])
#Si vous ne spécifiez pas les coordonnées des sommets dans la figure, ils seront disposés au hasard.
pos1 = {}
pos1["A"] = (0, 0)
pos1["B"] = (1, 0)
pos1["C"] = (1, 2)
pos1["D"] = (1, -2)
pos1["E"] = (2, 1)
pos1["F"] = (2, -1)
#Si vous le dessinez tel quel, le caractère de poids sera un obstacle, alors effacez-le
edge_labels = {(i, j): w['weight'] for i, j, w in G.edges(data=True)}
#Écrire le poids
nx.draw_networkx_edge_labels(G, pos1, edge_labels=edge_labels, font_size=18)
#Dessin
nx.draw_networkx(G, pos1, node_size=1000, label_pos=100, node_color="r", font_size=18)
#Enregistrer la figure
plt.savefig("dijkstra.png ")
plt.show()
#La méthode Dyxtra est également implémentée dans networkx. Le montant du calcul est O(V^2+E)Tellement lent
pred, dist = nx.dijkstra_predecessor_and_distance(G, "A")
print(pred, dist)
>>{'A': [], 'B': ['D'], 'C': ['B'], 'D': ['A'], 'F': ['B'], 'E': ['F']}
>>{'A': 0, 'D': 1, 'B': 3, 'C': 6, 'F': 6, 'E': 8}