Johnson-algoritmus
Kiejtés
- IPA: [ ˈjoxnʃonɒlɡoritmuʃ]
Főnév
Johnson-algoritmus
A Johnson-algoritmus egy gráfalgoritmus, amely a legrövidebb utak meghatározására szolgál iránymutatott, súlyozott gráfokban. Az algoritmus különösen hatékony ritka gráfok esetén, és képes negatív élhosszokat tartalmazó gráfokkal is működni (feltéve, hogy nincs negatív súlyú kör).
Alapötlet
A Johnson-algoritmus a Dijkstra-algoritmust és a Bellman-Ford-algoritmust kombinálja: 1. A Bellman-Ford-algoritmus segítségével először újrasúlyozza a gráf éleit, hogy az élhosszak ne legyenek negatívak. 2. Ezután a Dijkstra-algoritmust használja minden csúcsból kiinduló legrövidebb utak meghatározására az újrasúlyozott gráfban.
Lépések
1. Új csúcs hozzáadása
- Hozzáadunk egy új csúcsot ((s)) a gráfhoz.
- Az új csúcsot összekötjük az eredeti gráf minden csúcsával, (0) súlyú élekkel.
2. Bellman-Ford-algoritmus futtatása
- Az új csúcs ((s)) kiindulópontként használva a Bellman-Ford-algoritmus segítségével meghatározzuk az összes többi csúcs távolságát ((h(v))) az (s)-től.
- Ha a gráf tartalmaz negatív súlyú kört, az algoritmus leáll.
3. Él-súlyok újrasúlyozása
- Az új súlyokat az alábbi képlettel számítjuk: [ w’(u, v) = w(u, v) + h(u) - h(v) ] ahol:
- (w(u, v)) az eredeti él súlya,
- (h(u)) és (h(v)) a Bellman-Ford által számított súlyok.
- Ez az újrasúlyozás biztosítja, hogy minden (w’(u, v) ).
4. Dijkstra-algoritmus alkalmazása
- Az újrasúlyozott gráfban minden csúcsból futtatjuk a Dijkstra-algoritmust.
- Az eredeti súlyokat visszaszámítjuk az alábbi képlettel: [ d(u, v) = d’(u, v) - h(u) + h(v) ] ahol:
- (d’(u, v)) az újrasúlyozott gráfban számított távolság,
- (d(u, v)) az eredeti gráfban keresett távolság.
Pszeudokód
JohnsonAlgorithm(G): 1. Adj egy új csúcsot (s) a gráfhoz, és kösd össze minden csúccsal 0 súlyú élekkel. 2. Futtasd a Bellman-Ford algoritmust az (s)-ből: - Ha negatív súlyú kör található, térj vissza "HIBA". - Különben tárold a csúcsokhoz tartozó h értékeket. 3. Súlyok újraszámítása: w'(u, v) = w(u, v) + h(u) - h(v) 4. Távolságok számítása: - Minden csúcsból futtasd a Dijkstra-algoritmust az újrasúlyozott gráfban. - Az eredeti távolságok: d(u, v) = d'(u, v) - h(u) + h(v). 5. Térj vissza az összes legrövidebb úttal.
Python implementáció
import heapq
import math
def bellman_ford(graph, source, num_nodes):
distance = {node: math.inf for node in range(num_nodes)}
distance[source] = 0
for _ in range(num_nodes - 1):
for u in graph:
for v, w in graph[u]:
if distance[u] + w < distance[v]:
distance[v] = distance[u] + w
for u in graph:
for v, w in graph[u]:
if distance[u] + w < distance[v]:
raise ValueError("Negatív súlyú kör található!")
return distance
def dijkstra(graph, source, num_nodes):
distance = {node: math.inf for node in range(num_nodes)}
distance[source] = 0
pq = [(0, source)]
while pq:
curr_dist, u = heapq.heappop(pq)
if curr_dist > distance[u]:
continue
for v, w in graph[u]:
if distance[u] + w < distance[v]:
distance[v] = distance[u] + w
heapq.heappush(pq, (distance[v], v))
return distance
def johnson_algorithm(graph, num_nodes):
new_graph = graph.copy()
new_source = num_nodes
new_graph[new_source] = [(i, 0) for i in range(num_nodes)]
try:
h = bellman_ford(new_graph, new_source, num_nodes + 1)
except ValueError as e:
return str(e)
reweighted_graph = {}
for u in graph:
reweighted_graph[u] = []
for v, w in graph[u]:
reweighted_weight = w + h[u] - h[v]
reweighted_graph[u].append((v, reweighted_weight))
all_distances = {}
for u in range(num_nodes):
all_distances[u] = dijkstra(reweighted_graph, u, num_nodes)
for v in all_distances[u]:
if all_distances[u][v] < math.inf:
all_distances[u][v] += h[v] - h[u]
return all_distances
# Példa gráf
graph = {
0: [(1, 4), (2, 1)],
1: [(3, 1)],
2: [(1, 2), (3, 5)],
3: []
}
num_nodes = 4
distances = johnson_algorithm(graph, num_nodes)
print("Legrövidebb utak:")
for src, dist in distances.items():
print(f"{src}: {dist}")
Kimenet:
Legrövidebb utak: 0: {0: 0, 1: 3, 2: 1, 3: 4} 1: {0: inf, 1: 0, 2: inf, 3: 1} 2: {0: inf, 1: 2, 2: 0, 3: 3} 3: {0: inf, 1: inf, 2: inf, 3: 0}
Előnyök
- Hatékonyság ritka gráfok esetén:
- Az algoritmus (O(V^2 V + VE)) időben fut, ami ritka gráfoknál hatékony.
- Negatív élhosszok kezelése:
- A Bellman-Ford-algoritmus révén képes negatív súlyú élek kezelésére (negatív körök nélkül).
- Egyszerűség:
- A Bellman-Ford és a Dijkstra algoritmusok kombinációja jól ismert és könnyen implementálható.
Hátrányok
- Negatív súlyú körök:
- Az algoritmus nem működik, ha a gráf negatív súlyú köröket tartalmaz.
- Nagy memóriaigény:
- Nagy gráfok esetén az összes legrövidebb út tárolása jelentős memóriát igényelhet.
- Tömör gráfok esetén kevésbé hatékony:
- Tömör gráfok esetén más algoritmusok, mint a Floyd-Warshall, hatékonyabbak lehetnek.
Alkalmazások
- Hálózati útvonalak optimalizálása:
- Hálózati csomópontok közötti optimális útvonalak kiszámítása.
- Logisztika és szállítmányozás:
- Optimális útvonalak meghatározása szállítási rendszerekben.
- Rendszertervezés:
- Az összes csúcs közötti minimális költségek kiszámítása.
Összegzés
A Johnson-algoritmus hatékony eszköz az irányított, súlyozott gráfok legrövidebb útjainak meghatározására, különösen ritka gráfok esetében. Bár negatív súlyú élekkel is működik, a negatív körök jelenléte problémát okozhat. Ez a kombinált megközelítés a Dijkstra és a Bellman-Ford algoritmusok erejét használja fel a legrövidebb utak gyors és pontos kiszámításához.
- Johnson-algoritmus - Értelmező szótár (MEK)
- Johnson-algoritmus - Etimológiai szótár (UMIL)
- Johnson-algoritmus - Szótár.net (hu-hu)
- Johnson-algoritmus - DeepL (hu-de)
- Johnson-algoritmus - Яндекс (hu-ru)
- Johnson-algoritmus - Google (hu-en)
- Johnson-algoritmus - Helyesírási szótár (MTA)
- Johnson-algoritmus - Wikidata
- Johnson-algoritmus - Wikipédia (magyar)