Programação Dinâmica

9
Programação Dinâmica Aplicada em Problemas de Caminhos Mínimos Daniele C. Oliveira Programa de Pós Graduação em Computação - Faculdade de Computação– Universidade Federal de Uberlândia (UFU) [email protected] Abstract. This article describes some algorithms to find the shortest path between vertices of a graph using Dynamic Programming. The algorithms presented in this article are the Reliable Shortest Paths, the Floyd-Warshall algorithm to solve the problem All-pairs Shortest Paths and Held-Karp algorithm for solving the Traveling Salesman Problem. Resumo. Este artigo descreve alguns algoritmos para encontrar o caminho mínimo entre vértices de um grafo utilizando Programação Dinâmica. Os algoritmos apresentados neste artigo são o Shortest Reliable Paths, o Algoritmo de Floyd-Warshall para a resolução do problema All-pairs Shortest Paths e o Algoritmo de Held-Karp para resolução do Problema do Caixeiro Viajante. 1. Introdução Muitas vezes é necessário descobrir qual o menor caminho que liga um pouto ao outro em um grafo, ou o menor caminho entre duas cidades. Para resolver estes problemas é possível utilizar a Programação Dinâmica, que muitas vezes traz uma solução simples, ou mais rápida comparado à algoritmos utilizando recursão. Três problemas importantes, nesse contexto, envolve o problema do menor caminho confiável, muito utilizado em Redes de computadores, o problema dos menores caminhos entre quaisquer pares de vértices, e o problema do Caixeiro Viajante. Este artigo propõe apresentar uma solução conhecida para cada um desses problemas utilizando a Programação Dinâmica. O artigo mostrará a construção dos subproblemas para a criação do algoritmo.

Transcript of Programação Dinâmica

Page 1: Programação Dinâmica

Programação Dinâmica Aplicada em Problemas de Caminhos Mínimos

Daniele C. Oliveira

Programa de Pós Graduação em Computação - Faculdade de Computação– Universidade Federal de Uberlândia (UFU)

[email protected]

Abstract. This article describes some algorithms to find the shortest path between vertices of a graph using Dynamic Programming. The algorithms presented in this article are the Reliable Shortest Paths, the Floyd-Warshall algorithm to solve the problem All-pairs Shortest Paths and Held-Karp algorithm for solving the Traveling Salesman Problem.

Resumo. Este artigo descreve alguns algoritmos para encontrar o caminho mínimo entre vértices de um grafo utilizando Programação Dinâmica. Os algoritmos apresentados neste artigo são o Shortest Reliable Paths, o Algoritmo de Floyd-Warshall para a resolução do problema All-pairs Shortest Paths e o Algoritmo de Held-Karp para resolução do Problema do Caixeiro Viajante.

1. Introdução

Muitas vezes é necessário descobrir qual o menor caminho que liga um pouto ao outro em um grafo, ou o menor caminho entre duas cidades. Para resolver estes problemas é possível utilizar a Programação Dinâmica, que muitas vezes traz uma solução simples, ou mais rápida comparado à algoritmos utilizando recursão.

Três problemas importantes, nesse contexto, envolve o problema do menor caminho confiável, muito utilizado em Redes de computadores, o problema dos menores caminhos entre quaisquer pares de vértices, e o problema do Caixeiro Viajante.

Este artigo propõe apresentar uma solução conhecida para cada um desses problemas utilizando a Programação Dinâmica. O artigo mostrará a construção dos subproblemas para a criação do algoritmo.

Este trabalho está assim dividido: a Seção 2 apresentará o problema do Shortest Reliable Path, a Seção 3 apresentará o algoritmo Floyd-Warshall que resolve por Programação Dinâmica o problema All-pairs Shortest Paths, a Seção 4 apresentará o Problema do Caixeiro Viajante e o algoritmo de Held-Karp, por fim a Seção 5 apresentará as Conclusões.

2. Shortest Reliable Path

Muitos problemas em Redes de computadores envolvem encontrar o menor caminho para transmissão de pacotes de dados através de nós, como roteadores. Nesses casos simplesmente encontrar o caminho de menor custo, que é o que o algoritmo de Dijktra se propõe, não é suficiente. [1]

Page 2: Programação Dinâmica

O Algoritmo de Dijktra se preocupa em retornar o caminho de menor custo, ou seja, o caminho cuja somatória dos pesos das arestas é mínimo. O caminho retornado por esse algoritmo pode ser longo, ou seja, envolvendo muitas arestas. Por exemplo, a Figura 1 apresenta um Grafo Ponderado, com 6 nós. Se aplicarmos o algoritmo de Dijktra para encontrar o caminho mínimo entre o vértice inicial S e o vértice final T, seria retornado o caminho de custo 5 com 4 arestas:

S−A−B−D−T

No entanto, é possível traçar um caminho com apenas 2 arestas, cujo custo é um pouco maior que o caminho de Dijkstra. O caminho abaixo passa por apenas 2 arestas e tem custo igual a 6.

S−D−T

Figura 1 – Encontrar o caminho mínimo entre S e T com poucas arestas [1]

No caso de transmissão de pacotes por uma rede é provável que o segundo caminho seja mais vantajoso do que o primeiro, mesmo tendo custo maior. Isso se deve pelas incertezas que envolvem o percurso em redes de computadores, adicionar um salto no percurso pode acarretar, por exemplo, em perda de pacotes. [1]

Assim, o ideal é que o algoritmo seja capaz de retornar, além do caminho de custo mínimo, os caminhos com baixos custos e que utilizem no máximo um número k de arestas.

2.1 Shortest Reliable Paths em Programação Dinâmica

Para resolver o problema do Caminho mínimo confiável com Programação Dinâmica deve-se primeiro escolher um subproblema, de modo que toda informação vital para a resolução do problema seja armazenada e levada à diante [1].

Neste caso, pode-se definir:

Para cada vértice v e cada inteiro i≤ k , dist (v , i) é o tamanho do menor caminho entre S e v que utiliza i arestas.

Os valores iniciais de dist (v , 0) é ∞ para todos os vértices exceto S. dist ( S , 0 )=0

Com base nessas informações, pode-se definir o subproblema como:

dist ( v ,i )=min(u , v) {dist (u ,i−1 )+C (u , v ) }

Page 3: Programação Dinâmica

Aplicando o Algoritmo na Figura 1, pode-se gerar a matriz de distâncias da Tabela 1. As colunas identificam a quantidade de arestas utilizadas no percurso, enquanto que as linhas apresentam o vértice final do percurso. Pode-se observar que para alcançar o vértice T, é possível traçar um caminho (em amarelo) de k=4 e de custo igual a 5, esse é o caminho que o Algoritmo de Dijsktra retornaria. Além desse caminho, é possível traçar um caminho de k=2 (em azul) e um caminho de k=3, ambos de custo igual a 6.

Tabela 1. Aplicação do algoritmo Shortest Reliable Paths

K

0 1 2 3 4 5

Vér

tice

s

S 0 ∞ ∞ ∞ ∞ ∞

A ∞ 1 10 4 8 7

B ∞ ∞ 3 6 5 8

C ∞ 2 8 8 7 10

D ∞ 5 5 4 7 6

T ∞ ∞ 6 6 5 8

3. All-pairs Shortest Paths

Muitas vezes deseja-se descobrir o caminho mínimo que liga quaisquer pares de vértices de um grafo. Uma possível solução é utilizar o algoritmos geral de Caminho Mínimo, o tanto de vezes quanto for o tamanho do conjunto de vértices de um Grafo G(V , E). No entanto essa solução teria uma complexidade de O(|V|2|E|).

É possível criar uma solução para este problema com complexidade O(|V|3) utilizando Programação Dinâmica, essa solução é chamada de Algoritmo de Floyd-Warshall.

3.1. Algoritmo de Floyd-Warshall

O Algoritmo de Floyd-Warshall é baseado na seguinte situação: o menor caminho entre os vértices u e v utiliza um número de vértices intermediários, ou possivelmente nenhum vértice intermediário. Com base nisso, em um primeiro passo, será considerado que não será permitido usar vértices intermediários. Isso significa, que o menor caminho entre u e v será o custo da aresta que liga u a v, se existir. [1]

A partir desse ponto será expandido, gradualmente, o conjunto de vértices intermediários permitidos, até que todos os vértices sejam permitidos estar em todos os caminhos.

Mais formalmente, o problema será definido da seguinte forma [1]:

Seja V={1 ,2 , …, n } o conjunto de vértices do grafo

Page 4: Programação Dinâmica

dist (i , j , k ) é o tamanho do caminho mínimo entre i e j, em que apenas os nós {1 , 2, …, k } são permitidos como intermediários

Inicialmente, dist (i , j , 0) será igual ao custo da aresta que liga i a j, se existir, e ∞ caso contrário.

O próximo passo é definir o subproblema que auxiliará na resolução do problema All-pairs Shortest Paths. O subproblema, então, será expandir o conjunto de nós intermediários adicionando o nó 𝑘. Ao adicionar esse novo vértice, será necessário reexaminar todos os pares i , j para verificar se ao usar o vértice k como um nó intermediário será produzido um caminho mais curto [1].Neste ponto, já são conhecidos os tamanhos do menor caminho de i até k e de k até j, que foram previamente calculados em subproblemas anteriores [1]. Assim, como mostra a Figura 2, é possível verificar qual o caminho será mais curto, usando ou não o vértice k .

Figura 2 – Comparação dos caminhos entre i a j, usando ou não o vértice intermediário k. [1]

Assim, k será usado para compor o caminho de i a j se e somente se a condição abaixo seja satisfeita [1]:dist ( i , k , k−1 )+dist ( k , j ,k−1 )<dist (i , j , k−1)

O Algoritmo completo de Floyd-Marshall está apresentado no Quadro 1, e como pode ser observado tem complexidade O(|V|3).Quadro 1. Algoritmo Floyd-Marshall [1]

for i = 1 to n:for j = 1 to n:

dist(i, j, 0) = ∞for all (i, j) ∈ E:

dist(i, j, 0) = C(i, j)for k = 1 to n:

for i = 1 to n:for j = 1 to n:

dist(i, j, k) = min{dist(i, k, k-1) + dist(k, j, k-1), dist(i, j, k-1)}

Page 5: Programação Dinâmica

4. The Traveling Salesman Problem

Um problema muito conhecido e bastante difundido é o Problema do Caixeiro Viajante (The Traveling Salesman Problem). O problema é definido assim: “Um Caixeiro deve visitar apenas uma vez todas as 𝑛 diferentes cidades partindo de uma cidade base, e retornando à ela. Qual caminho minimiza a distância total percorrida pelo caixeiro?” [2]

Ou seja, deseja-se saber qual o percurso de menor distância passando por todas as cidades, visitando apenas uma vez por cada uma, e retornar ao ponto de origem. Este é um problema considerado NP-Completo, portanto muito difícil de ser resolvido, inclusive computacionalmente [2]. No Grafo da Figura 3 é difícil, mas possível calcular que o percurso ótimo teria tamanho igual a 10.

Figura 3 – O percurso do Caixeiro Viajante ótimo tem tamanho 10 [1]

Usualmente o problema do Caixeiro Viajante é dado por: Seja G=(V , D) um Grafo Ponderado, onde [3]:

V={1,2 , …,n } é o conjunto de cidades D é a matriz de distancias entre cidades, com ∀ i , i∈V ,i ≠ j , dij>0

O problema consiste em encontrar o percurso com distância mínima, começando em 1, passando por todas as n cidades e retornando a 1. Existem diversas maneiras de resolver o problema, e até hoje são pesquisadas outras formas, aplicando diferentes técnicas.

Um forma é resolver o problema aplicando a força bruta, que consistiria em verificar todos os percursos possíveis, a fim de retornar o menor. Considerando que todas possibilidades de percurso é dada pela fórmula (n−1 )!, a complexidade dessa solução seria O(n!) [1].

Outra forma de resolver o problema é utilizar a Programação Dinâmica. Como será visto na seção 4.1, essa solução é bem mais rápida do que a força bruta, apesar de não ser polinomial.

4.1. O Caixeiro Viajante em Programação Dinâmica

A solução do Problema do Caixeiro Viajante por Programação Dinâmica foi proposta por Held, Karp em 1962. Essa solução ficou conhecida pelos seus nomes, Algoritmo Held-Karp.

Page 6: Programação Dinâmica

A solução é baseada na seguinte situação “Suponha que o percurso inicia na cidade 1, visita-se algumas cidades parando quando alcançar a cidade 𝑖, faltando 𝑘 cidades para visitar antes de retornar a 1.” [2]

Para formular o subproblema é necessário definir quais informações devem ser utilizadas para ampliar o caminho. Primeiramente é necessário conhecer o vértice i em questão, pois ele ajudará a determinar qual será a próxima cidade mais conveniente para a próxima visita. Além disso, é necessário, também conhecer quais cidades já foram visitadas, de forma a não repeti-las. [1]

A partir dessas informações é possível definir o subproblema [1]: “Dado um subconjunto de cidades S⊆V , que inclui a cidade 1, e uma cidade i∈S , j≠ 1 , seja C (S , j) o tamanho do menor caminho que passa por cada nó de 𝑆 exatamente uma vez, começando em 1 e terminando em j.”

A partir desse subproblema é possível construir o algoritmo. Considerando:

Se ¿ S∨¿2, então C (S , j)=d ij, para j=2 ,3 , …, n Se ¿ S∨¿2, então C (S , j)=o caminho ó timo de1 at é i+d ij ,∃i∈S−{ j}

Assim, tem-se que:

C (S , j)=mini∈S , i ≠ j {C (S−{ j},i)+d ij}

O Algoritmo Held-Karp pode ser visto no Quadro 2. É possível observar que a complexidade do algoritmo é O(n22n). O Algoritmo divide o problema em 2n subconjuntos de vértices possíveis analisados, o cálculo de C (S , j) é feito para n vértices, e por fim essas instruções são executadas n vezes [4].

Quadro 2. Algoritmo Held-Karp [1]

5. Conclusão

O uso da Programação Dinâmica, muitas vezes produz um algoritmo mais rápido do que as soluções recursivas existentes. Foi possível observar, que a formulação dos subproblemas é bastante simples e eficiente. No caso do Caixeiro Viajante, não é conhecido, ainda, uma solução polinomial. A Programação Dinâmica traz um algoritmo exponencial, que ainda assim é tem menor complexidade comparado ao algoritmo de Força Bruta.

C ({1 } ,1)=0 for s=2¿n :

paratodos os subconjuntos S⊆ {1 ,2 , …, n } de tamanho se contendo1 : C (S ,1)=∞ for all j∈S , j≠ 1:

C ( S , j )=mini∈S , i ≠ j {C (S− { j }, i )+d ij } return min j C ( {1 , …, n } , j )+d j 1

Page 7: Programação Dinâmica

References

[1] Dasgupta, S., Papadimitriou, C. H., & Vazirani, U. V. (2006). Algorithms.

[2] Bellman, R. (1962). Dynamic programming treatment of the travelling salesman problem. Journal of the ACM (JACM), 9(1), 61-63.

[3] Bellmore, M., & Nemhauser, G. L. (1968). The traveling salesman problem: a survey. Operations Research, 16(3), 538-558.

[4] Held, M., & Karp, R. M. (1962). A dynamic programming approach to sequencing problems. Journal of the Society for Industrial & Applied Mathematics, 10(1), 196-210.