Algoritmos de Caminho Mínimo Parte 1 A journey of a thousand miles starts with a single step and if that step is the right step, it becomes the last step. Index 1. Introduction 2. Applications 3. Tree of Shortest Paths 4. Shortest Path Problems in Acyclic Networks 5. Dijkstra s Algorithm 6. Dial s Implementation 7. Heap Implementation 8. Radix Heap Implementation 1. Introduction Problemas de caminhos mínimos aparecem freqüentemente na prática, sempre que queremos transportar algum material entre dois pontos em uma rede o mais rápido e barato quanto for possível. Eles aparecem também como subproblemas na resolução de problemas de otimização de redes e combinatória. Vamos considerar uma rede direcionada G = (N, A), com custo de arco c ij associado com cada arco (i, j) pertencente a A. A rede possui um nó diferenciado s chamado source ou nó de partida. Seja A(i) a lista de arcos adjacentes do nó i e seja C = max{c ij : (i,j) em A}. Nós consideramos o tamanho do caminho direcionado como a soma dos custos de todos os arcos no caminho. Nós podemos interpretar o problema de caminho mínimo como o problema de enviar uma unidade de fluxo com o menor custo possível (com custo de fluxo nos arcos c ij ) do nó s para cada um dos nós em N {s} em uma rede sem capacidades. Com isso podemos estabelecer a seguinte formulação de programação linear para o problema de caminho mínimo: Minimizar (i,j) A c ij x ij n 1 para i = s Sujeito à {j (i,j) A} x ij - {j (j,i) A} x ji = 1 para todo i em N {s} Para o nosso estudo de problemas de caminho mínimo vamos assumir algumas propriedades: 1. Todos os custos dos arcos são inteiros. 2. A rede contém um caminho direcionado de s para todos os nós do grafo. 3. A rede não contém um ciclo negativo (i.e., um ciclo direcionado de tamanho negativo). 4. A rede é direcionada.
Os problemas de caminho mínimo podem ser, basicamente, de dois tipos: de ponto de partida único (encontrar o caminho mínimo de um nó a todos os outros) e de pares múltiplos (encontrar o caminho mínimo entre quaisquer pares de pontos da rede). Os algoritmos para resolução de problemas de caminhos mínimos são classificados em Label-Setting e Label-Correcting. O primeiro é aplicado para problemas de caminho mínimo definidos em redes acíclicas, com tamanhos arbitrários de arcos e maiores que zero. Algoritmos de Label-Setting convergem para a solução através da inserção de um arco ótimo ao caminho em cada iteração. Já os algoritmos de Label-Correcting são mais gerais e podem ser aplicados a todas as classes de problemas, inclusive aqueles com arcos negativos. Eles convergem para o caminho mínimo considerando todos os arcos como temporários até a última iteração, quando todos se tornam permanentes. Uma breve comparação entre os dois tipos de algoritmos mostra que os de Label-Setting são mais eficientes, mas oferecem menos flexibilidade e generalidade de operação. Nesse capítulo vamos focar na solução de algoritmos de Label-Setting. 2. Applications a. Approximating Piecewise Lienear Functions. Diversas aplicações em vários campos científicos usam funções particionadas. Entretanto, em algumas situações, essas funções apresentem um grande número de pontos de parada. Nesse caso, pode ser interessante o uso de uma aproximação da função com menos pontos de parada. Essa aproximação pode ocupar menos espaço de memória e reduzir o custo de aplicação da função. O preço que se paga é o erro da aproximação. Nosso desafio é identificar um subconjunto de pontos que definam a função de aproximação com o maior ganho de eficiência e armazenamento e o menor erro possível. Nós vamos formular esse problema como um problema de caminho mínimo em uma rede G com n nós, numerados de 1 a n. A rede contém um arco (i, j) para cada par de nós i e j com i < j. O arco (i, j) significa que nós aproximamos os segmentos da função original entre os pontos a i, a i+1,..., a j por um segmento linear ligando os pontos a i e a j. O custo c ij do arco (i, j) tem duas componentes: o custo de armazenamento e a penalização de aproximar todos os pontos entre i e j. No intervalo [x i, x j ], a função de aproximação é f 2 (x) = f 1 (x) + (x x i ) [f 1 (x j ) f 1 (x i )] / (x j x i ). Com isso, o custo total do intervalo será: j c ij = α + β (f 1 (x k ) f 2 (x k )) 2 k=i,onde f 1 (x) é a função original, f 2 (x) é a aproximação, α é o custo de armazenamento da função e β uma constante. Cada caminho direcionado partindo do nó 1 ao nó n corresponde à função f2(x) e o tamanho do caminho equivale ao custo total de armazenamento da função e para
aproximar a função original. Assim, o caminho mínimo entre os nós 1 e n especifica o conjunto ótimo de pontos necessários para definir a função de aproximação. b. Allocation Inspection Effort on a Production Line. O problema aqui é, dado uma linha de produção onde cada estágio i tem uma chance a i de produzir um defeito, encontrar o plano ótimo de inspeção que especifique em que estágios devemos inspecionar os itens de forma a minimizar o custo de produção e inspeção. Poucas estações de verificação de erros reduzem o custo de inspeção, mas aumentam o custo de produção, dado que precisamos realizar operações desnecessárias sobre produtos que já estão defeituosos. Suponha a existência dos seguintes dados de custo: P i, o custo de fabricação por unidade no estágio i. Fij, o custo fixo de realizar inspeções após o estágio j, dado que a última inspeção foi realizada no estágio i. Gij, o custo de inspecionar uma unidade após o estágio j, dado que a última inspeção foi no estágio i. Nós podemos formular esse problema como um problema de caminho mínimo em uma rede com (n + 1) nós, numerados de 0,1,..., n. A rede contém um arco (i, j) para cada par de nós i e j, com i < j. Por exemplo, o caminho 0-2-4 implica que nós realizamos inspeções depois dos estágios 2 e 4. Tomando B(i) = B i k=1 (1 a k ) como o número de elementos não defeituosos no final do estágio i, nós associamos o seguinte custo c ij com qualquer arco (i, j) na rede: j cij = f ij + B(i)g ij + B(i) k=i +1 p k É fácil ver que o custo cij denota o custo total nos estágios i + 1, i + 2,..., j. Os primeiros dois termos da soma são os custos de inspeção e o terceiro é o custo de produção nesse estágio. 3. Tree of Shortest Paths
Vamos mostrar com as propriedades abaixo que os algoritmos de caminho mínimo que vamos estudar geram uma árvore direcionada de saída com a propriedade de que o único caminho saindo do nó de partida em direção a todos os outros nós é o caminho mínimo. O principal corolário dessa propriedade é que o espaço de armazenamento gasto para guardar o caminho mínimo entre quaisquer dois nós se resume ao espaço gasto para guardar a árvore de saída do algoritmo. Propriedade 4.3.1: Se o caminho s = i 1 i 2... i h = k é o caminho mínimo do nó s ao nó k, então para cada q = 2, 3,..., h 1, o sub-caminho s = i 1 i 2... i q é o caminho mínimo do nó s até o nó i q. Propriedade 4.3.2: Seja d um vetor representando as distâncias do caminho mínimo. Então, um caminho direcionado P saindo do nó de partida até o nó k é o caminho mínimo se e somente se d(j) = d(i) + c ij para cada arco (i, j) em P. 4. Shortest Path Problems in Acyclic Networks Em redes acíclicas temos um método para resolver o problema do caminho mínimo em O(m). O algoritmo é bem simples. Primeiro, nós setamos d(s) = 0 e os outros labels de distância para um valor muito grande. Então, nós examinamos os nós em uma ordem topológica, e para cada nó i sendo examinado, nós examinamos os arcos em A(i). Se para algum arco (i, j) em A(i), nós encontrarmos algum d(j) > d(i) + c ij, então nós setamos d(j) = d(i) + c ij. Quando o algoritmo terminar de examinar todos os nós uma vez nessa ordem, os marcadores de distância serão ótimos. 5. Dijkstra s Algorithm O algoritmo de Dijkstra mantém um marcador de distância d(i) para cada nó i, que é o limite superior do tamanho do caminho mínimo até o nó i. Em cada passo intermediário, o algoritmo divide os nós em dois grupos: aqueles que são designados como permanentemente marcados (ou permanentes) e os marcados temporariamente (ou temporários). O marcador de distância para cada nó permanente é o tamanho do caminho mínimo do nó source até aquele nó. Para cada nó temporário o marcador de distância é um limite superior do tamanho do caminho mínimo do nó de partida até aquele nó. A idéia básica do algoritmo é a cada iteração marcar um novo nó como permanente de acordo com a sua distância ao nó s e, em seguida, atualizar os custos dos nós adjacentes ao novo nó inserido no caminho. Quando todos os nós estiverem presentes no caminho, as distâncias entre o nó s e qualquer outro nó serão mínimas.