Análise e Síntese de Algoritmos Caminhos Mais Curtos CLRS, Cap. 24 Contexto • Algoritmos elementares em grafos • Grafos não dirigidos: – Árvores abrangentes de menor custo • Grafos dirigidos: – Caminhos mais curtos • Fonte única • Entre todos os pares • Redes de fluxo (grafos dirigidos com arcos capacitados) – Fluxos máximos – Fluxos de custo mínimo 2003/2004 Análise e Síntese de Algoritmos 2 Resumo • Caminhos Mais Curtos – Definições • Caminhos Mais Curtos com Fonte Única (SSSPs) – Propriedades • Relaxações – Algoritmo de Dijkstra – Algoritmo de Bellman-Ford – SSSPs em DAGs 2003/2004 Análise e Síntese de Algoritmos 3 Caminhos Mais Curtos • Dado um grafo G = (V, E), dirigido, com uma função de pesos w : E R, define-se o peso de um caminho p, p = v0,v1,…,vk, como a soma dos pesos dos arcos que compõem p: k w p w v i1, v i i 1 • O peso do caminho mais curto de u para v é definido por: p min w p : u v u, v se existe caminho de u para v caso contrário • Um caminho mais curto de u para v é qualquer caminho p tal que w(p) = (u,v) 2003/2004 Análise e Síntese de Algoritmos 4 Problemas de Caminhos Mais Curtos • Caminhos Mais Curtos com Fonte Única (SSSPs) – Identificar o caminho mais curto de um vértice fonte s V para qualquer outro vértice v V • Caminhos Mais Curtos com Destino Único – Identificar o caminho mais curto de qualquer vértice v V para um vértice destino t V • Caminho Mais Curto entre Par Único – Identificar caminho mais curto entre dois vértices u e v • Caminhos Mais Curtos entre Todos os Pares (APSPs) – Identificar um caminho mais curto entre cada par de vértices de V 2003/2004 Análise e Síntese de Algoritmos 5 Ciclos Negativos • Arcos podem ter pesos com valor negativo • É possível a existência de ciclos com peso total negativo – Se ciclo negativo não atingível a partir da fonte s (s,v) bem definido – Se ciclo negativo atingível a partir da fonte s • pesos dos caminhos mais curtos não são bem definidos • é sempre possível encontrar um caminho mais curto de s para qualquer vértice incluído no ciclo • define-se (s,v) = - 2003/2004 Análise e Síntese de Algoritmos 6 Ciclos Negativos (Cont.) • Identificação de ciclos negativos: – Alguns algoritmos requerem pesos não negativos • Dijkstra – Alguns algoritmos identificam ciclos negativos e reportam a sua existência • Bellman-Ford • Exemplo: s 3 3 x -5 2003/2004 y z -2 w(s,x,y,z) = 4 w(s,x,y,x,y,z) = 2 w(s,x,y,x,y,x,y,z) = 0 … Análise e Síntese de Algoritmos 7 Representação de Caminhos Mais Curtos • Para cada vértice v V associar predecessor [v] – Após identificação dos caminhos mais curtos, [v] identifica vértice anterior a v em caminho mais curto de s para v • Sub-grafo de predecessores G = (V, E): V v V : v NIL s E v , v E : v V s 2003/2004 Análise e Síntese de Algoritmos 8 Representação de Caminhos Mais Curtos (Cont.) • Uma árvore de caminhos mais curtos é um sub-grafo dirigido G’ = (V’, E’), V’ V e E’ E, tal que: – V’ é o conjunto de vértices atingíveis a partir de s em G – G’ forma uma árvore com raiz s – Para todo o v V’, o único caminho de s para v em G’ é um caminho mais curto de s para v em G • OBS: Após identificação dos caminhos mais curtos de G, G’ é dado por G = (V, E) • OBS: Dado G = (V, E), G’ não é necessariamente único 2003/2004 Análise e Síntese de Algoritmos 9 Estudo dos SSSPs • Sub-estrutura óptima: (sub-caminhos de caminhos mais curtos são caminhos mais curtos) 24.1 – Seja p = v1,v2,…,vk um caminho mais curto entre v1 e vk, e seja pij = vi,vi+1,…,vj um sub-caminho de p entre vi e vj. Então pij é um caminho mais curto entre vi e vj • Se existisse caminho mais curto entre vi e vj então seria possível construir caminho entre v1 e vk mais curto do que p; uma contradição ! 2003/2004 Análise e Síntese de Algoritmos 10 Estudo dos SSSPs – Seja p = s,…,v um caminho mais curto entre s e v, que pode ser decomposto em p’ = s,…,u e (u,v). Então (s,v) = (s,u) + w(u,v) • p’ é caminho mais curto entre s e u 24.1 • (s,v) = w(p) = w(p’) + w(u,v) = (s,u) + w(u,v) 24.10 – Para todos os arcos (u,v) E verifica-se (s,v) (s,u) + w(u,v) • Caminho mais curto de s para v não pode ter mais peso do que qualquer outro caminho de s para v • Assim, peso do caminho mais curto de s para v não superior ao peso do caminho mais curto de s para u seguido do arco (u,v) (i.e. exemplo de um dos caminhos de s para v) 2003/2004 Análise e Síntese de Algoritmos 11 Estudo dos SSSPs (Cont.) • Relaxação: – Algoritmos para SSSPs utilizam relaxação – d[v]: limite superior no valor do peso do caminho mais curto entre s e v; estimativa do caminho mais curto Initialize-Single-Source(G,s) for v V[G] d[v] = [v] = NIL d[s] = 0 Relax(u,v,w) if d[v] > d[u] + w(u,v) d[v] = d[u] + w(u,v) [v] = u – Algoritmos para SSSPs aplicam sequência de relaxações dos arcos de G após inicialização 2003/2004 Análise e Síntese de Algoritmos 12 Estudo dos SSSPs (Cont.) • Propriedades da relaxação: 24.13 – Após relaxar arco (u,v), d[v] d[u] + w(u,v) • Se d[v] > d[u] + w(u,v) antes da relaxação, então d[v] = d[u] + w(u,v) após relaxação • Se d[v] d[u] + w(u,v) antes da relaxação, então d[v] d[u] + w(u,v) após relaxação • Em qualquer caso, d[v] d[u] + w(u,v) após relaxação 2003/2004 Análise e Síntese de Algoritmos 13 Estudo dos SSSPs (Cont.) • Propriedades da relaxação: 24.11 – d[v] (s, v) para qualquer v V e para qualquer sequência de passos de relaxação nos arcos de G. Se d[v] atinge o valor (s,v) então o valor não é mais alterado • d[v] (s, v) é válido após inicialização • Prova por contradição. – Seja v o primeiro vértice para o qual a relaxação do arco (u,v) causa d[v] < (s, v) 24.10 – Após relaxar arco: d[u] + w(u,v) = d[v] < (s, v) (s, u) + w(u,v) – Pelo que, d[u] < (s, u) antes da relaxação de (u,v); contradição ! • Após ter d[v] = (s, v), o valor de d[v] não pode decrescer; e pela relaxação também não pode crescer ! 2003/2004 Análise e Síntese de Algoritmos 14 Estudo dos SSSPs (Cont.) • Propriedades da relaxação: 24.14 – Seja p = s,…,u,v um caminho mais curto em G, e seja Relax(u,v,w) executada no arco (u,v). Se d[u] = (s, u) antes da chamada a Relax(u,v,w), então d[v] = (s, v) após a chamada a Relax(u,v,w) • Se d[u] = (s, u) então valor de d[u] não é mais alterado • Após relaxar arco (u,v): d[v] d[u] + w(u,v) = (s, u) + 24.1 w(u,v) = (s, v) • Mas, d[v] (s, v), pelo que d[v] = (s, v), e não se altera ! 24.11 2003/2004 Análise e Síntese de Algoritmos 15 Estudo dos SSSPs (Revisão) 24.1 – Seja p = v1,v2,…,vk um caminho mais curto entre v1 e vk, e seja pij = vi,vi+1,…,vj um sub-caminho de p entre vi e vj. Então pij é um caminho mais curto entre vi e vj – Seja p = s,…,v um caminho mais curto entre s e v, que pode ser decomposto em p’ = s,…,u e (u,v). Então (s,v) = (s,u) + w(u,v) 24.10 – Para todos os arcos (u,v) E verifica-se (s,v) (s,u) + w(u,v) 2003/2004 Análise e Síntese de Algoritmos 16 Estudo dos SSSPs (Revisão) • Propriedades da relaxação: 24.13 24.11 24.14 – Após relaxar arco (u,v), d[v] d[u] + w(u,v) – d[v] (s, v) para qualquer v V e para qualquer sequência de passos de relaxação nos arcos de G. Se d[v] atinge o valor (s,v) então o valor não é mais alterado – Seja p = s,…,u,v uma caminho mais curto em G, e seja Relax(u,v,w) executada no arco (u,v). Se d[u] = (s, u) antes da chamada a Relax(u,v,w), então d[v] = (s, v) após a chamada a Relax(u,v,w) – … 2003/2004 Análise e Síntese de Algoritmos 17 Algoritmo de Dijkstra — Organização • Todos os arcos com pesos não negativos • Algoritmo Greedy • Algoritmo mantém conjunto de vértices S com pesos dos caminhos mais curtos já calculados • A cada passo algoritmo selecciona vértice u em V - S com menor estimativa do peso do caminho mais curto – vértice u é inserido em S – arcos que saem de u são relaxados • Fila de prioridade Q é mantida e actualizada com operações de relaxação 2003/2004 Análise e Síntese de Algoritmos 18 Algoritmo de Dijkstra — Pseudo-Código O(V) Dijkstra(G,w,s) Initialize-Single-Source(G,s) S= Q = V[G] while Q u = Extract_Min(Q) S = S {u} foreach v Adj[u] Relax(u,v,w) • Exemplo 2003/2004 // Fila de prioridade Q O(lg V) // Actualizar Q O(E) Análise e Síntese de Algoritmos O(lg V) 19 Algoritmo de Dijkstra vs. Alg. de Prim MST-Prim(G,w,r) Q = V[G] // Fila de prioridade Q foreach u Q // Inicialização key[u] = key[r] = 0 [r] = NIL while Q u = Extract_Min(Q) foreach v Adj[u] if v Q and w(u,v) < key[v] [v] =u key[v] = w(u,v) // Q é actualizada! 2003/2004 Análise e Síntese de Algoritmos 20 Algoritmo de Dijkstra — Complexidade • Extract-Min e actualização de chaves na fila de prioridade – O(lg V) cada operação, em amontoado binário (binary heap) • Extract-Min executado O(V) vezes • Número de vezes que chaves são actualizadas é O(E) – OBS: cada arco é analisado apenas uma vez, independentemente do ciclo while exterior, e não causa mais do que uma actualização do amontoado ! • Tempo de execução é: O((V+E) lg V) – Se fila de prioridade utiliza um amontoado binário 2003/2004 Análise e Síntese de Algoritmos 21 Algoritmo de Dijkstra — Correcção • Como resultado da aplicação do algoritmo de Dijkstra, d[u] = (s,u) para u V[G] 24.6 – Provar que d[u] = (s,u) quando u adicionado ao conjunto S, e que a igualdade é posteriormente mantida – Prova por contradição. Seja u o primeiro vértice tal que quando u é adicionado ao conjunto S, d[u] (s,u) • u s, porque d[s] = (s,s) = 0 • s S, pelo que S quando u adicionado a S – Existe caminho de s para u (caso contrário, d[u] = (s,u) = ; uma contradição) • existe caminho mais curto p de s para u • y: primeiro vértice de p em V-S • x: predecessor de y, último vértice de p em S 2003/2004 Análise e Síntese de Algoritmos 22 Algoritmo de Dijkstra — Correcção – Quando u é adicionado a S, d[y] = (s,y) • x pertence a S • d[x] = (s,x) (porque u é o primeiro vértice para o qual igualdade não se verifica) • Quando x adicionado a S, relaxação causa d[y] = (s,y), 24.14 porque caminho mais curto para y passa por x – y aparece antes de u num caminho mais curto, pelo que (s,y) (s,u) (pesos não negativos) 24.11 • d[y] = (s,y) (s,u) d[u] • Mas y e u em V-S, e u escolhido antes de y, pelo que d[u] d[y] • d[y] = (s,y) = (s,u) = d[u], o que contradiz escolha de u ! • Necessariamente, valor de d[u] mantém-se após d[u] = (s,u) 24.11 2003/2004 Análise e Síntese de Algoritmos 23 Algoritmo de Dijkstra — Condições • Pesos dos arcos têm que ser não negativos: 6 s 1 u v 3 w -5 1 • Execução do algoritmo de Dijkstra: – Analisar w (com d[w] = 3) – Analisar v (com d[v] = 4) – Analisar u (com d[u] = 6) • Corrigir d[v] para 1 • Estimativa de w incorrecta (final=3; correcto=2) ! 2003/2004 Análise e Síntese de Algoritmos 24 Algoritmo de Bellman-Ford — Organização • Permite pesos negativos e identifica existência de ciclos negativos • Baseado em sequência de passos de relaxação • Apenas requer manutenção da estimativa associada a cada vértice 2003/2004 Análise e Síntese de Algoritmos 25 Algoritmo de Bellman-Ford — Pseudo-Código (V E) Bellman-Ford(G,w,s) Initialize-Single-Source(G,s) for i = 1 to |V[G]|-1 foreach (u,v) E[G] Relax(u,v,w) foreach (u,v) E[G] if d[v] > d[u] + w(u,v) // Ciclo negativo return FALSE // Sem ciclos negativos return TRUE • Exemplo 2003/2004 Análise e Síntese de Algoritmos 26 Algoritmo de Bellman-Ford — Complexidade • Por inspecção: – Inicialização: (V) – Ciclo for: • Tempo de execução é (VE) devido aos dois ciclos, em V e em E – OBS: para cada i, todos os arcos de E são relaxados ! 2003/2004 Análise e Síntese de Algoritmos 27 Algoritmo de Bellman-Ford — Correcção • Se G = (V,E) não contém ciclos negativos, então após a 24.2 aplicação do algoritmo de Bellman-Ford, d[v] = (s,v) para todos os vértices atingíveis a partir de s – Seja v atingível a partir de s, e seja p = vo,v1,…,vk um caminho mais curto entre s e v, com v0 = s e vk = v • p é simples, pelo que k |V|-1 – Objectivo: provar por indução que para i = 0,1,…,k, d[vi] = (s,vi) após iteração i sobre os arcos de G, e que valor não é alterado posteriormente 24.11 • Base: d[v0] = (s,v0) = 0 após inicialização (e não se altera) • Passo indutivo: assumir d[vi-1] = (s,vi-1) após iteração (i-1) – Arco (vi-1,vi) relaxado na iteração i, pelo que d[vi] = (s,vi) após iteração i (ver propriedades da relaxação) (e não se altera) 24.14 2003/2004 Análise e Síntese de Algoritmos 28 Algoritmo de Bellman-Ford — Correcção • Se G=(V,E) não contém ciclos negativos, o algoritmo de 24.4 Bellman-Ford retorna TRUE. Se o grafo contém algum ciclo negativo atingível a partir de s, o algoritmo retorna FALSE – Sem ciclos negativos, resultado anterior assegura que para qualquer arco (u,v) de E, d[v] d[u] + w(u,v), pelo que teste do algoritmo falha para todo o (u,v) e o valor retornado é TRUE – Na presença de pelo menos um ciclo negativo atingível a partir de s, c = vo,v1,…,vk, onde vo = vk k w v i1, v i 0 i 1 – Prova por contradição. Admitir que algoritmo retorna TRUE • Pelo que d[vi] d[vi-1] + w(vi-1,vi), para i =1,…,k 2003/2004 Análise e Síntese de Algoritmos 29 Algoritmo de Bellman-Ford — Correcção – Somando desigualdades através do ciclo c: k k k i 1 i 1 i 1 dv i dv i1 w v i1, v i – Nota: k k dv i dv i1 i 1 i 1 Por ser um ciclo ! k – Obtendo-se: 0 w v i1, v i i 1 – O que contradiz a hipótese de existência de um ciclo negativo algoritmo retorna FALSE. 2003/2004 Análise e Síntese de Algoritmos 30 Algoritmos de Dijkstra e Bellman-Ford • Dijkstra: – Só permite pesos não negativos – Tempo de execuçao é O((V + E) lg V) • Bellman-Ford – Permite pesos negativos e identifica ciclos negativos – Tempo de execução é (V E) 2003/2004 Análise e Síntese de Algoritmos 31 SSSPs em DAGs — Organização DAG-Shortest-Paths(G,w,s) Ordenação topológica dos vértices de G Initialize-Single-Source(G,s) foreach u V[G] por ordem topológica foreach v Adj[u] Relax(u,v,w) • Tempo de execução: O(V+E) • Exemplo 2003/2004 Análise e Síntese de Algoritmos 32 SSSPs em DAGs — Correcção • Dado G = (V, E),dirigido, acíclico, como resultado do 24.5 algoritmo, d[v] = (s,v) para todo o v V – Se v não atíngivel de s, então d[v] = (s,v) = – Seja p = v0,v1,,vk um caminho mais curto, com v0 = s e vk = v – Ordem topológica implica arcos analisados por ordem (v0, v1), (v1, v2), …, (vk-1, vk) 24.11 • Cada arco é relaxado apenas uma vez – Valor de d[v] não é alterado após d[v] = (s,v) – Por indução, d[vi] = (s,vi) sempre que cada vértice vi é terminado • Base: Estimativa de s não alterada; d[s] = d[v0] = (s,v0) = 0 • Indução: d[vi-1] = (s,vi-1) após terminar análise de vi-1 – Relaxação de (vi-1, vi) causa d[vi] = (s,vi), pelo que d[vi] = (s,vi) após terminar análise de vi 24.14 2003/2004 Análise e Síntese de Algoritmos 33 Revisão • Caminhos Mais Curtos com Fonte Única (SSSPs) – – – – Definições e Propriedades Algoritmo de Dijkstra Algoritmo de Bellman-Ford SSSPs em DAGs • A seguir: – Caminhos Mais Curtos entre Todos os Pares 2003/2004 Análise e Síntese de Algoritmos (CLRS, Cap. 25) 34