En aquest tema, explorarem els algoritmes que ens permeten trobar el camí més curt entre dos nodes en un graf. Aquests algoritmes són fonamentals en moltes aplicacions, com ara la navegació GPS, les xarxes de comunicació i la logística. Ens centrarem en els següents algorismes:
- Algoritme de Dijkstra
- Algoritme de Bellman-Ford
- Algoritme de Floyd-Warshall
- Algoritme de Johnson
- Algoritme de Dijkstra
Descripció
L'algoritme de Dijkstra és un algorisme de recorregut en grafs que troba el camí més curt des d'un node origen a tots els altres nodes en un graf amb pesos no negatius.
Pseudocodi
Dijkstra(G, s) per a cada node v en G dist[v] := infinit anterior[v] := no definit dist[s] := 0 Q := tots els nodes de G mentre Q no estigui buit u := node en Q amb dist[u] mínim eliminar u de Q per a cada veí v de u alt := dist[u] + pes(u, v) si alt < dist[v] dist[v] := alt anterior[v] := u retornar dist[], anterior[]
Exemple
Considerem el següent graf:
Aplicarem l'algoritme de Dijkstra des del node A.
Implementació en Python
import heapq def dijkstra(graph, start): dist = {node: float('inf') for node in graph} dist[start] = 0 pq = [(0, start)] prev = {node: None for node in graph} while pq: current_dist, current_node = heapq.heappop(pq) if current_dist > dist[current_node]: continue for neighbor, weight in graph[current_node].items(): distance = current_dist + weight if distance < dist[neighbor]: dist[neighbor] = distance prev[neighbor] = current_node heapq.heappush(pq, (distance, neighbor)) return dist, prev graph = { 'A': {'B': 1, 'C': 4}, 'B': {'A': 1, 'C': 2, 'D': 2}, 'C': {'A': 4, 'B': 2, 'D': 1}, 'D': {'B': 2, 'C': 1} } distances, previous_nodes = dijkstra(graph, 'A') print("Distàncies:", distances) print("Nodes anteriors:", previous_nodes)
Exercici Pràctic
Donat el següent graf, aplica l'algoritme de Dijkstra per trobar el camí més curt des del node 'S' a tots els altres nodes:
- Algoritme de Bellman-Ford
Descripció
L'algoritme de Bellman-Ford és capaç de trobar el camí més curt des d'un node origen a tots els altres nodes en un graf, fins i tot si aquest conté arestes amb pesos negatius. A més, pot detectar cicles de pes negatiu.
Pseudocodi
BellmanFord(G, s) per a cada node v en G dist[v] := infinit anterior[v] := no definit dist[s] := 0 per i de 1 a |V| - 1 per a cada aresta (u, v) en G si dist[u] + pes(u, v) < dist[v] dist[v] := dist[u] + pes(u, v) anterior[v] := u per a cada aresta (u, v) en G si dist[u] + pes(u, v) < dist[v] error "Graf conté un cicle de pes negatiu" retornar dist[], anterior[]
Exemple
Considerem el següent graf amb pesos negatius:
Aplicarem l'algoritme de Bellman-Ford des del node A.
Implementació en Python
def bellman_ford(graph, start): dist = {node: float('inf') for node in graph} dist[start] = 0 prev = {node: None for node in graph} for _ in range(len(graph) - 1): for node in graph: for neighbor, weight in graph[node].items(): if dist[node] + weight < dist[neighbor]: dist[neighbor] = dist[node] + weight prev[neighbor] = node for node in graph: for neighbor, weight in graph[node].items(): if dist[node] + weight < dist[neighbor]: raise ValueError("Graf conté un cicle de pes negatiu") return dist, prev graph = { 'A': {'B': 1, 'C': -3}, 'B': {'A': 1, 'C': 2, 'D': 2}, 'C': {'A': -3, 'B': 2, 'D': 1}, 'D': {'B': 2, 'C': 1} } distances, previous_nodes = bellman_ford(graph, 'A') print("Distàncies:", distances) print("Nodes anteriors:", previous_nodes)
Exercici Pràctic
Donat el següent graf amb pesos negatius, aplica l'algoritme de Bellman-Ford per trobar el camí més curt des del node 'S' a tots els altres nodes:
- Algoritme de Floyd-Warshall
Descripció
L'algoritme de Floyd-Warshall és un algorisme per trobar els camins més curts entre tots els parells de nodes en un graf. És especialment útil per a grafs densos.
Pseudocodi
FloydWarshall(G) dist := array de |V| x |V| inicialitzat a infinit per a cada node v en G dist[v][v] := 0 per a cada aresta (u, v) en G dist[u][v] := pes(u, v) per k de 1 a |V| per i de 1 a |V| per j de 1 a |V| si dist[i][j] > dist[i][k] + dist[k][j] dist[i][j] := dist[i][k] + dist[k][j] retornar dist
Exemple
Considerem el següent graf:
Aplicarem l'algoritme de Floyd-Warshall.
Implementació en Python
def floyd_warshall(graph): nodes = list(graph.keys()) dist = {node: {node2: float('inf') for node2 in nodes} for node in nodes} for node in nodes: dist[node][node] = 0 for node in graph: for neighbor, weight in graph[node].items(): dist[node][neighbor] = weight for k in nodes: for i in nodes: for j in nodes: if dist[i][j] > dist[i][k] + dist[k][j]: dist[i][j] = dist[i][k] + dist[k][j] return dist graph = { 'A': {'B': 1, 'C': 4}, 'B': {'A': 1, 'C': 2, 'D': 2}, 'C': {'A': 4, 'B': 2, 'D': 1}, 'D': {'B': 2, 'C': 1} } distances = floyd_warshall(graph) print("Distàncies:", distances)
Exercici Pràctic
Donat el següent graf, aplica l'algoritme de Floyd-Warshall per trobar els camins més curts entre tots els parells de nodes:
- Algoritme de Johnson
Descripció
L'algoritme de Johnson és una combinació dels algorismes de Bellman-Ford i Dijkstra que permet trobar els camins més curts entre tots els parells de nodes en un graf amb pesos negatius, però sense cicles de pes negatiu.
Pseudocodi
Johnson(G) afegir un node q a G amb arestes de pes 0 a tots els altres nodes h := BellmanFord(G, q) per a cada aresta (u, v) en G pes(u, v) := pes(u, v) + h[u] - h[v] eliminar q de G per a cada node u en G dist[u] := Dijkstra(G, u) per a cada node v en G dist[u][v] := dist[u][v] + h[v] - h[u] retornar dist
Exemple
Considerem el següent graf amb pesos negatius:
Aplicarem l'algoritme de Johnson.
Implementació en Python
def bellman_ford(graph, start): dist = {node: float('inf') for node in graph} dist[start] = 0 prev = {node: None for node in graph} for _ in range(len(graph) - 1): for node in graph: for neighbor, weight in graph[node].items(): if dist[node] + weight < dist[neighbor]: dist[neighbor] = dist[node] + weight prev[neighbor] = node for node in graph: for neighbor, weight in graph[node].items(): if dist[node] + weight < dist[neighbor]: raise ValueError("Graf conté un cicle de pes negatiu") return dist, prev def dijkstra(graph, start): dist = {node: float('inf') for node in graph} dist[start] = 0 pq = [(0, start)] prev = {node: None for node in graph} while pq: current_dist, current_node = heapq.heappop(pq) if current_dist > dist[current_node]: continue for neighbor, weight in graph[current_node].items(): distance = current_dist + weight if distance < dist[neighbor]: dist[neighbor] = distance prev[neighbor] = current_node heapq.heappush(pq, (distance, neighbor)) return dist, prev def johnson(graph): new_graph = {node: neighbors.copy() for node, neighbors in graph.items()} new_graph['q'] = {node: 0 for node in graph} h, _ = bellman_ford(new_graph, 'q') for u in graph: for v in graph[u]: graph[u][v] += h[u] - h[v] del new_graph['q'] dist = {} for u in graph: dist[u], _ = dijkstra(graph, u) for v in dist[u]: dist[u][v] += h[v] - h[u] return dist graph = { 'A': {'B': 1, 'C': -3}, 'B': {'A': 1, 'C': 2, 'D': 2}, 'C': {'A': -3, 'B': 2, 'D': 1}, 'D': {'B': 2, 'C': 1} } distances = johnson(graph) print("Distàncies:", distances)
Exercici Pràctic
Donat el següent graf amb pesos negatius, aplica l'algoritme de Johnson per trobar els camins més curts entre tots els parells de nodes:
Conclusió
En aquesta secció, hem explorat diversos algorismes per trobar els camins més curts en grafs. Hem après a utilitzar l'algoritme de Dijkstra per a grafs amb pesos no negatius, l'algoritme de Bellman-Ford per a grafs amb pesos negatius, l'algoritme de Floyd-Warshall per a trobar els camins més curts entre tots els parells de nodes, i l'algoritme de Johnson per a grafs amb pesos negatius però sense cicles de pes negatiu. Aquests algorismes són fonamentals en moltes aplicacions pràctiques i ens proporcionen eines poderoses per resoldre problemes complexos en grafs.
Algoritmes Avançats
Mòdul 1: Introducció als Algoritmes Avançats
Mòdul 2: Algoritmes d'Optimització
- Programació Lineal
- Algoritmes d'Optimització Combinatòria
- Algoritmes Genètics
- Optimització de Colònia de Formigues
Mòdul 3: Algoritmes en Grafs
- Representació de Grafs
- Cerca en Grafs: BFS i DFS
- Algoritmes de Camins Mínims
- Algoritmes de Flux Màxim
- Algoritmes d'Aparellament en Grafs
Mòdul 4: Algoritmes de Cerca i Ordenació
Mòdul 5: Algoritmes d'Aprenentatge Automàtic
- Introducció a l'Aprenentatge Automàtic
- Algoritmes de Classificació
- Algoritmes de Regressió
- Xarxes Neuronals i Deep Learning
- Algoritmes de Clustering
Mòdul 6: Casos d'Estudi i Aplicacions
- Optimització en la Indústria
- Aplicacions de Grafs en Xarxes Socials
- Cerca i Ordenació en Grans Volums de Dades
- Aplicacions d'Aprenentatge Automàtic en la Vida Real