1
Fork 0
mirror of https://github.com/Steffo99/unisteffo.git synced 2024-11-23 00:14:21 +00:00
triennale-appunti-steffo/public/materials/year1/algoritmi/25_AlgoritmoDiDijkstra.md

69 lines
3 KiB
Markdown
Raw Normal View History

2022-02-03 02:15:39 +00:00
# Algoritmo di Dijkstra
L'_Algoritmo di [Dijkstra](https://upload.wikimedia.org/wikipedia/commons/8/85/Dijkstra.ogg)_ è un algoritmo che risolve il problema del **percorso più breve da una sorgente singola** per grafi con pesi **reali positivi** `\mathbb{R}^+`.
L'algoritmo trova tutti i percorsi più brevi per raggiungere qualsiasi nodo del grafo partendo da un dato nodo, assieme al costo richiesto per farlo.
## Funzionamento
1. Separiamo tutti i nodi del grafo in due gruppi: **visitati** e **non visitati**.
- Tutti i nodi partono da **non visitati**.
2. Per ogni nodo, manteniamo un valore "**costo richiesto per raggiungerlo**", che verrà cambiato man mano che l'algoritmo avanza.
- Il costo di partenza è `+∞`.
- Il costo sarà **definitivo per i nodi visitati**, e **provvisorio per i non visitati**.
3. Creiamo un insieme detto _frontiera_ che conterrà tutti i **nodi non visitati adiacenti** a quelli visitati.
4. Prendiamo il nodo iniziale, che avrà un **costo di `0`**, e definiamolo il nodo _attuale_.
5. Finchè ci sono dei nodi non sono stati visitati, ripetiamo il seguente ciclo:
1. Aggiungiamo i nodi adiacenti al nodo attuale alla frontiera.
- Il costo per raggiungerli sarà il **costo per il nodo attuale sommato al costo dell'arco** che li connette al nodo attuale.
Se questo **costo** risulta essere **minore del costo provvisorio** precedente, esso **diventerà il nuovo costo**.
- Questa operazione è detta _rilassamento dell'arco_.
2. Facciamo diventare **visitato** il nodo attuale.
- Il percorso che abbiamo fatto per raggiungerlo è obbligatoriamente il più breve.
3. Il prossimo nodo attuale sarà il nodo di frontiera con un costo più basso.
- Per questo, è possibile definire l'algoritmo di Dijkstra come un **algoritmo greedy**.
### Non funziona se...
L'algoritmo smette di funzionare nel caso in cui siano presenti **costi negativi** e il grafo non sia **aciclico**, in quanto non saremmo mai in grado di rendere visitato un nodo.
## Costo computazionale
| Categoria | Upper bound |
|-----------|-------------|
| Tempo | `O(nodi + archi) log nodi)` |
### Scomposizione
- Inizializzazione: `O(nodi)`
- Creazione coda priorità: `O(nodi log nodi)`
- Ciclo: `O((nodi + archi) log nodi)`
## Pseudocodice
```python
import math
class Info:
def __init__(self, distance=math.inf, previous=None):
self.distance = distance
self.previous = previous
def dijkstra(graph, start):
data = [Info() for node in graph.nodes]
queue = PriorityQueue([start])
while queue:
node = queue.pop()
for arc in node.connections:
other = arc.other(node)
if data[node.number].distance + arc.cost < data[other].distance:
data[other].distance = data[node.number].distance + arc.cost
queue.decrease_priority_for(other, data[other].distance)
data[v].previous = node
return data
```
## Visualizzazione
[Visualgo](https://visualgo.net/en/sssp)