Aula 17 - Distância em Grafos Valorados: Algoritmo de Dijkstra
Universidade Federal do Rio Grande do Sul
Instituto de Informática
Departamento de Informática Teórica
Estes slides utilizam conteúdo adaptado da bibliografia da disciplina e também de notas de aula e slides prévios dos professores Bruno Grisci, Rodrigo Machado e André Grahl.
Problema do Caminho Mínimo com Origem Única
Entrada:
- Um grafo direcionado G=(V,E)
- Cada arco tem um custo não negativo l_e para e\in E.
- Vértice de origem s.
Saída: Para cada v\in V:
- L(v)= custo do menor caminho entre s-v em G (distância).
Importante:
P. Custo do caminho 0-6? 25.
P. Custo do caminho 0-7? 8.
Algoritmo ingênuo: calcula todos os caminhos e seus respectivos pesos totais, buscando o menor peso total.
Quantos caminhos entre dois nodos específicos num K_n?
\boxed{\displaystyle \sum_{k=0}^{n-2} \binom{n-2}{k} k! = \sum_{k=0}^{n-2}\frac{(n-2)!}{(n-2-k)!} = (n-2)! \sum_{i=0}^{n-2} \frac{1}{i!} \;\approx\; e \cdot (n - 2)!}
Como visto anteriormente para grafos não valorados:
Custo: O(m+n).
Entrada: um grafo G=(V,E) com m=|E| e n=|V|:
P. É possível usar Busca em Largura?
Ideia: substituir todo arco por arcos de tamanho 1 e usar BFS.
R. O grafo pode crescer muito, e.g., peso 10^{10}.
Exemplo:
Entrada: o grafo G abaixo, com pesos l_e \geq 0 nas arestas, e o vértice de origem s = S (em azul).
Saída: para cada v \in V, a distância d(S,v) do menor caminho de S até v (caminho entre parênteses):
\begin{array}{|ll|} \hline \mathbf{dist_0} & \\ \color{blue}S & \color{blue}0 \\ A & 1 \\ B & 3 \\ C & \infty \\ D & \infty \\ E & \infty \\ F & \infty \\ \hline \color{blue}\mathbf{X_0} & \\ \color{blue}\{S\} & \\ \hline \color{red}\text{min:} & A \\ \hline \end{array}
\begin{array}{|ll|} \hline \mathbf{dist_1} & \\ \color{blue}S & \color{blue}0 \\ \color{blue}A & \color{blue}1 \\ B & 3 \\ C & 5 \\ D & 6 \\ E & \infty \\ F & \infty \\ \hline \color{blue}\mathbf{X_1} & \\ \color{blue}\{S,A\} & \\ \hline \color{red}\text{min:} & B \\ \hline \end{array}
\begin{array}{|ll|} \hline \mathbf{dist_2} & \\ \color{blue}S & \color{blue}0 \\ \color{blue}A & \color{blue}1 \\ \color{blue}B & \color{blue}3 \\ C & 4 \\ D & 6 \\ E & \infty \\ F & \infty \\ \hline \color{blue}\mathbf{X_2} & \\ \color{blue}\{S,A,B\} & \\ \hline \color{red}\text{min:} & C \\ \hline \end{array}
\begin{array}{|ll|} \hline \mathbf{dist_3} & \\ \color{blue}S & \color{blue}0 \\ \color{blue}A & \color{blue}1 \\ \color{blue}B & \color{blue}3 \\ \color{blue}C & \color{blue}4 \\ D & 6 \\ E & 10 \\ F & \infty \\ \hline \color{blue}\mathbf{X_3} & \\ \color{blue}\{S,A,B,C\} & \\ \hline \color{red}\text{min:} & D \\ \hline \end{array}
\begin{array}{|ll|} \hline \mathbf{dist_4} & \\ \color{blue}S & \color{blue}0 \\ \color{blue}A & \color{blue}1 \\ \color{blue}B & \color{blue}3 \\ \color{blue}C & \color{blue}4 \\ \color{blue}D & \color{blue}6 \\ E & 10 \\ F & \infty \\ \hline \color{blue}\mathbf{X_4} & \\ \color{blue}\{S,A,B,C,D\} & \\ \hline \color{red}\text{min:} & E \\ \hline \end{array}
\begin{array}{|ll|} \hline \mathbf{dist_5} & \\ \color{blue}S & \color{blue}0 \\ \color{blue}A & \color{blue}1 \\ \color{blue}B & \color{blue}3 \\ \color{blue}C & \color{blue}4 \\ \color{blue}D & \color{blue}6 \\ \color{blue}E & \color{blue}8 \\ F & \infty \\ \hline \color{blue}\mathbf{X_5} & \\ \color{blue}\{S,A,B,C,D,E\} & \\ \hline \color{red}\text{para!} & \\ \hline \end{array}
Exemplo:
Responda:
P. Como reduzir o problema do caminho mínimo com custos negativos para o mesmo problema com custos não negativos?
Observação 1. Não preserva os caminhos mínimos.
Observação 2. Dijkstra também não funciona no exemplo.
Teorema. Para todo grafo direcionado com arcos de custos não negativos, o algoritmo de Dijkstra computa todos os caminhos mínimos à partir da origem.
d[v]=L(v), \forall v\in V, onde:
Prova. Por indução no número de iterações do algoritmo.
Invariante. Ao fim de cada iteração, d[v] = L(v) para todo v \in X.
Caso base. Antes do laço, X = \{s\} e d[s] = 0 = L(s) (pesos \geq 0).
Hipótese de indução (HI). No início da iteração, d[v] = L(v) \forall v \in X.
Iteração atual. O algoritmo escolhe o arco da fronteira (\textcolor{#3a7a10}{v^*}, \textcolor{#3a7a10}{w^*}) que minimiza d[v] + \ell_{vw} e faz d[\textcolor{#3a7a10}{w^*}] = d[\textcolor{#3a7a10}{v^*}] + \ell_{\textcolor{#3a7a10}{v^*}\textcolor{#3a7a10}{w^*}}.
Como \textcolor{#3a7a10}{P^*} é um caminho de custo d[\textcolor{#3a7a10}{w^*}], já sabemos que L(\textcolor{#3a7a10}{w^*}) \leq d[\textcolor{#3a7a10}{w^*}].
Falta mostrar: nenhum caminho de s a \textcolor{#3a7a10}{w^*} custa menos que d[\textcolor{#3a7a10}{w^*}].
Por contradição. Suponha que existe um caminho \textcolor{#d9690b}{P} de s a \textcolor{#3a7a10}{w^*} com \text{custo}(\textcolor{#d9690b}{P}) < d[\textcolor{#3a7a10}{w^*}].
Contradição! Então nenhum caminho é mais curto: d[\textcolor{#3a7a10}{w^*}] = L(\textcolor{#3a7a10}{w^*}) e o invariante vale para X \cup \{\textcolor{#3a7a10}{w^*}\}.
Término. Cada iteração adiciona um vértice a X, então o laço executa no máximo n - 1 vezes.
Ao final, pelo invariante, d[v] = L(v) para todo v \in X. E os vértices fora de X?
Conclusão. d[v] = L(v) para todo v \in V: o algoritmo de Dijkstra computa todas as distâncias mínimas a partir de s. \blacksquare
P. Qual o tempo de execução?
Ideia Principal
Uma implementação direta mantém, para cada vértice, uma variável booleana indicando se ele pertence ao conjunto X.
A cada iteração:
- Percorrem-se todas as arestas (v, w), com v \in X e w \notin X;
- Calcula-se o valor de Dijkstra \text{dist}(v) + \ell_{vw} em tempo constante por aresta;
- Escolhe-se a aresta com o menor valor e adiciona-se seu vértice destino a X.
Análise de Complexidade
São no máximo n - 1 iterações, cada uma percorrendo m arestas: T(n, m) = \mathcal{O}(n) \times \mathcal{O}(m) = \mathcal{O}(mn)
Python 3.12.3, Ubuntu 24.04.2 LTS, Intel® Core™ i7-4810MQ × 8

“In their capacity as a tool, computers will be but a ripple on the surface of our culture. In their capacity as intellectual challenge, they are without precedent in the cultural history of mankind.”
Algoritmo de Bellman-Ford: resolve o problema para grafos com um vértice de origem e arestas que podem ter pesos negativos.
Algoritmo de Floyd-Warshall: determina a distância entre todos os pares de vértices de um grafo com arestas que podem ter pesos negativos (mas não ciclo negativos).
Algoritmo de Johnson: determina a distância entre todos os pares de vértices de um grafo em grafos esparsos. Pode haver arestas negativas, mas não ciclos negativos.
Caminhos mínimos em DAGs: grafo acíclico dirigido.’’
Algoritmo A^*: um algoritmo de busca heurística que calcula o caminho mínimo com um vértice de origem.
?