Baixe o app para aproveitar ainda mais
Prévia do material em texto
1 BC-0506: Comunicação e Redes Algoritmos em Grafos Santo André, 2Q2011 Parte 1: Algoritmos de Busca 3 Rediscutindo: Representações em Grafos Matriz de Adjacências Matriz de Incidências Lista de Adjacências 4 Matriz de Adjacências A matriz de adjacências de um grafo tem colunas e linhas indexadas pelos vértices. Se A for a matriz de adjacências de índices i e j, A(i,j) = 1 se os vértices i e j forem conectados por uma aresta e A(i,j) = 0 caso contrário. Em grafos não orientados a matriz de adjacências é sempre simétrica, ou seja, A(i,j) = A(j,i). 5 Exemplo: Grafo não-orientado e Matriz de Adjacência 6 Exemplo: Grafo orientado e Matriz de Adjacência 7 Matriz de Incidência A Matriz de Incidência representa computacionalmente um grafo em que as linhas e colunas são vértices e arestas. Assim, dado um grafo com n vértices e m arestas, podemos representá-lo por uma matriz B n x m, guardando informações sobre a incidência de vértices em cada aresta. Para representar um grafo sem pesos nas arestas e não-orientado, basta que as entradas da matriz B contenham +1 se a aresta chega no vértice, -1 se a aresta parte do vértice e 0 caso a aresta não chegue nem parta no/do vértice. 8 Exemplo: Matriz de Incidência 9 Lista de Adjacências O vetor de listas de adjacência de um grafo tem uma lista encadeada para cada um de seus vértices. A lista de cada vértice v contém todos os vértices vizinhos que se pode alcançar a partir de v. 10 Exemplo: Lista de Adjacências Adj[1] = {2} Adj[2] = {1,3,5} Adj[3] = {4} Adj[4] = {1,5} Adj[5] = {2} 11 Grafos Ponderados Um grafo pode ser ponderado ou não-ponderado. Se todas as arestas apresentarem um mesmo peso (ou custo), diz-se que o grafo é não ponderado e considera-se todas as arestas com custo igual a 1. O grafo será ponderado se diferentes arestas possuírem custos distintos. 12 Exemplo: Grafos Ponderados e Não-Ponderados Pode-se ainda representar um grafo não-ponderado ignorando os custos das arestas. Em um grafo não- ponderado, todas as arestas possuem custo igual a 1. Em grafos ponderados, representa-se entre parênteses o custo da aresta. 13 Caminhos em Grafos Um caminho num grafo é uma sequência de vértices dotada da propriedade de que cada novo vértice é adjacente ao anterior. A origem de um caminho é o primeiro vértice do caminho. O término é o último vértice. Diz-se que um caminho com origem s e término t vai de s a t. Dizemos que uma aresta v → w pertence a um caminho se o vértice w é o sucessor de v no caminho. Todas as arestas de um caminho apontam no mesmo sentido, de um vértice para o seu sucessor. 14 Caminhos em Grafos Costuma-se diferenciar caminho de passeio em grafos. Assim, em um caminho de s a t, não pode haver nenhum vértice repetido, enquanto em um passeio isso pode acontecer. O comprimento de um caminho é o número de termos da sequência menos um. Em outras palavras, é o número de arestas do caminho. Em grafos não-orientados, a existência de caminhos é uma propriedade simétrica: para quaisquer dois vértices s e t, existe caminho de s a t, se e somente se, existe caminho de t a s. 15 Exemplo de Caminho Para o exemplo a seguir (não-orientado e não- ponderado), o caminho 1 a 3 deve ser passando pelo vértice 2. Caminho 1 a 3: 1 → 2 → 3 Caminho 3 a 1: 3 → 2 → 1 16 Exemplo de Caminho Para grafos orientados (ponderados ou não) o caminho de s a t pode ser diferente do caminho de t a s. Exemplo: Caminho 1 a 3: 1 → 2 → 3 Caminho 3 a 1: 3 → 4 → 1 17 Busca em Grafos Um algoritmo de busca (ou varredura) é um algoritmo que examina, sistematicamente, todos os vértices e todas as arestas de um grafo. Cada aresta é examinado uma só vez. Depois de visitar a ponta inicial de uma aresta, o algoritmo percorre aresta e visita sua ponta final. Para justificar a palavra "busca", devemos imaginar que o algoritmo percorre o grafo buscando todos os vértices que são acessíveis a partir de um determinado vértice em questão. 18 Busca em Grafos Há muitas maneiras de fazer uma tal busca. Cada estratégia de busca é caracterizada pela ordem em que os vértices são visitados. Assim, a ordem de visita torna-se essencial se desejamos determinar outras propriedades além da mera característica de um determinado vértice ser alcançado a partir de outro. 19 Busca em Grafos O algoritmo de busca em grafos pode então nos mostrar outras características de grafos. Os algoritmos mais comumente discutidos são os algoritmos de busca em largura e de busca em profundidade, que serão apresentados a seguir 20 Busca em Largura No algoritmo de busca em largura, a lista de vértices obedece a política FIFO (First-In-First-Out, ou o primeiro a entrar será o primeiro a sair – Fila): o vértice que sai da lista é sempre o que está lá há mais tempo. O algoritmo pinta de preto todos os vértices que podem ser alcançados a partir de um vértice de origem até alcançar o vértice de destino. A principal característica desse algoritmo é que a busca segue um modelo de parametrização pela distância a partir da origem. No exemplo a seguir, vamos supor que estejamos buscando o caminho entre 1 e 5. 21 Exemplo: Busca em Largura No início todos os vértices são pintados de branco. Para o exemplo o vértice de origem é o 1, sendo marcado com “largura” igual a zero. O algoritmo pinta de cinza este vértice e o coloca em uma fila Q, mapeando também seus vizinhos. Neste caso, ele se conecta apenas com o vértice 2. Q = 1 22 Exemplo: Busca em Largura No próximo passo, o vértice 1 é retirado da fila Q e pintado de preto, enquanto o vértice 2 é mapeado com “largura” igual a 1 e inserido na fila Q. O vértice 2 é então pintado de cinza e seus vizinhos são mapeados. No caso, os vértices 1, 3 e 5. Q = 2 23 Exemplo: Busca em Largura O vértice 2 é retirado da fila Q e pintado de preto, enquanto os vértices 3 e 5 são colocados na fila Q e mapeados com “largura” igual a 2. O vértice 1 não é colocado na fila (apesar de haver uma conexão), pois já está pintado de preto. Q = 3, 5 24 Exemplo: Busca em Largura O vértice 3 é retirado da fila Q e pintado de preto, enquanto o vértice 4 é colocado na fila Q e mapeado com “largura” igual a 3. Q = 5, 4 25 Exemplo: Busca em Largura Em seguida o vértice 5 também é visitado, pintado de preto e retirado da fila Q. Como sua única conexão é com o vértice 2 (já pintado de preto), nada é feito. Q = 4 26 Exemplo: Busca em Largura No término da execução deste exemplo o vértice 4 é pintado de preto e retirado da fila Q. Como suas conexões levam a vértices já pintados de preto (1 e 5), nada é feito e a fila torna-se vazia (fim do algoritmo). Q vazia 27 Exemplo: Busca em Largura Para o caminho entre origem e destino parte-se do vértice de destino, examinado seus predecessores até chegar à origem. Depois inverte-se o vetor conseguido. Para este exemplo, o caminho é 1 →2→5. A seguir é apresentado um pseudocódigo do algoritmo de busca em largura. Q vazia 28 Pseudocódigo: Busca em Largura Busca_largura(Adj,origem,destino) // Adj é a lista de adjacências do grafo t = tamanho de Adj; para u = 1 até t, cor(u) = branco; fim cor(origem) = cinza; fila(1) = origem; i = 1; f = 2; enquanto i < f, u = fila(i); i = i + 1; c = Adj{u}; se (c não é vazio), para v = 1 até tamanho de c, se cor(c(v)) == branco, cor(c(v)) = cinza; predecessor(c(v)) = u; fila(f) = c(v); f = f + 1; fim cor(u) = preto; fim fim fim caminho(1) = destino; prev = predecessor(destino);enquanto (prev ~= origem), caminho(tamanho do caminho + 1) = prev; destino = prev; prev = predecessor(destino); fim caminho(tamanho do caminho + 1) = prev; para i = 1 até tamanho de caminho caminho2(i) = caminho(tamanho do caminho-i+1); fim 29 Busca em Profundidade A estratégia seguida pela busca em profundidade é, como seu nome implica, procurar cada vez mais fundo no grafo. Nessa busca as arestas são exploradas a partir do vértice v mais recentemente descoberto que ainda possui arestas inexploradas saindo dele. Quando todas as arestas alcançadas a partir do vértice de origem são visitadas a busca regressa para explorar as arestas que deixam o vértice do qual v foi descoberto. Para o exemplo a seguir, tentaremos descobrir o caminho de 1 a 5. 30 Exemplo: Busca em Profundidade No início todos os vértices são brancos, mas partindo do vértice 1, este é pintado de cinza e sua aresta é examinada, levando ao vértice 2. O vértice 1 é marcado com distância 0. 31 Exemplo: Busca em Profundidade O vértice 1 é pintado de preto. O vértice 2 é pintado de cinza, enquanto suas arestas serão examinadas e é atribuído a ele uma distância 1. A primeira aresta de 2 leva ao vértice 1, já pintado de preto. Partindo para a segunda aresta, esta leva ao vértice 3. 32 Exemplo: Busca em Profundidade O vértice 3 é então pintado de cinza e a ele é atribuído uma distância 2. Sua única aresta é examinada, levando ao vértice 4. 33 Exemplo: Busca em Profundidade O vértice 3 é pintado de preto e o vértice 4 é pintado de cinza, sendo que a ele é atribuído uma distância 3. Suas arestas serão examinadas. A primeira leva a um nó já pintado de preto, logo não é analisada. A segunda, leva ao vértice 5. 34 Exemplo: Busca em Profundidade O vértice 4 é pintado de preto e o vértice 5 é pintado de cinza, sendo que a ele é atribuído uma distância 4. Sua aresta é examinada, constatando-se que leva a um vértice já pintado de cinza. 35 Exemplo: Busca em Profundidade O vértice 5 é pintado de preto e parte-se para o exame da última aresta pertencente ao vértice 2. 36 Exemplo: Busca em Profundidade Como neste momento esta aresta remete a um vértice já pintado de preto (vértice 5), o vértice 2 também é pintado de preto e o algoritmo é terminado. Diferente da busca em largura, este algoritmo produz um caminho 1 → 2 → 3 → 4 →5 para o caminho de 1 a 5. 37 Busca em Profundidade: Detalhes O algoritmo de busca em profundidade na sua forma mais robusta utiliza um recurso computacional chamado recursão, em que uma dada função “chama a si mesma”. Um pseudocódigo para o algoritmo de busca em largura que encontra o caminho entre dois vértices é mostrado a seguir. Claramente há uma grande diferença no modo de implementação e nos resultados dos dois algoritmos. Desta forma, quando há a necessidade de obtenção do caminho mínimo entre dois vértices, outros algoritmos podem ser utilizados. 38 Pseudocódigo: Busca em Profundidade Busca_profundidade(Adj,origem,destino) //Adj é a lista de adjacências do grafo t = tamanho de Adj; para u = 1 até t, cor(u) = branco; fim caminho(1) = origem; c = busca_em_prof(Adj,origem,destino,caminho); função busca_em_prof(Adj,u,d,caminho) cor(u) = cinza; v = Adj[u]; caminho2 = caminho; se (v não é vazio), para i = 1 até tamanho(v), se (caminho(tamanho(caminho))) != d), se (v(i) igual a d), cor(v(i)) = preto; caminho = caminho2; caminmho(tamanho(caminho2)+1) = d; Salto; senão se (cor(v(i)) == branco), caminho = caminho2; caminho(tamanho(caminho2)+1) = v(i); c = busca_em_prof(Adj,v(i),d,caminho); caminho = c; fim fim fim cor(u) = preto; fim 39 Distância Um caminho C num grafo é mínimo se não existe outro caminho com mesma origem e mesmo término que C mas comprimento menor que o de C. A distância de um vértice s a um vértice t num grafo é o comprimento de um caminho mínimo de s a t. Se não existe caminho algum de s a t, a distância de s a t é infinita. A distância de s a t é d se e somente se (1) existe um caminho de comprimento d de s a t e (2) nenhum caminho de s a t tem comprimento menor que d. Em geral, num grafo direcionado a distância de um vértice s a um vértice t é diferente da distância de t a s. Se o grafo é não- orientado, entretanto, as duas distâncias são iguais. 40 Grafos Ponderados Muitas aplicações associam um número a cada aresta de um grafo. Diremos que esse número é o custo da aresta, que pode ser remetido a uma característica física da conexão. Por exemplo, se duas arestas de um grafo representam conexões de cabos óticos entre cidades A, B, e C (vértices do grafo), sendo que a distância entre A e B é o dobro da distância de A a C, então pode-se atribuir um custo maior à aresta que liga os vértices A e B. Dependendo da aplicação, pode ser mais apropriado dizer "peso" ou "comprimento" no lugar de "custo". 41 Exemplo: Caminho de Custo Mínimo Se o grafo ponderado e direcionado abaixo for tomado como exemplo, o caminho mínimo C de 1 a 5 tem custo d igual a 2, levando a um caminho 1 → 2 → 5. Perceba que existe um outro caminho 1 → 2 → 3 → 4 → 5, mas este possui custo d igual a 5. Parte 2: Algoritmos de Caminhos Mínimos 43 Busca de Caminhos Mínimos Para a busca de caminhos mínimos em grafos há algoritmos específicos que executam a tarefa. Entretanto há formas específicas para tratar o problema, sendo diferentes quando busca-se caminhos mínimos a partir de um dado vértice ou quando se buscam os caminhos mínimos entre todos os pares de vértices. 44 Caminho Mínimo com Origem Fixa Problema dos Caminhos Mínimos com Origem Fixa: dado um vértice s de um grafo com custos nos arcos, encontrar, para cada vértice t que pode ser alcançado a partir de s, um caminho mínimo simples de s a t. Todos os algoritmos para esses problemas exploram a seguinte propriedade básica: Propriedade Triangular: Para quaisquer vértices x, y e z de um grafo com custos não-negativos nos arcos, tem- se d(x,z) ≤ d(x,y) + d(y,z) , sendo d(i,j) a distância de i a j. 45 Algoritmo de Dijkstra Um algoritmo eficiente para a obtenção do caminho mínimo em grafos com custos não- negativos é o chamado algoritmo de Dijkstra. O algoritmo pode ser usado, em particular, para encontrar um caminho de custo mínimo de um dado vértice a outro, ou a todos os outros vértices. O algoritmo de Dijkstra funciona de forma iterativa, passo a passo, como mostrado a seguir. A entrada é a matriz de adjacências do grafo. 46 Algoritmo de Dijkstra 1. Como primeiro passo é atribuída uma distância para todos os pares de vértices. De início é atribuída uma distância infinita para todos, menos para o vértice de origem. 2. Marque todos os vértices como não visitados e defina o vértice inicial como vértice corrente. 3. Para este vértice corrente, considere todos os seus vértices vizinhos não visitados e calcule a distância a partir do vértice. Se a distância for menor do que a definida anteriormente, substitua a distância. 4. Quando todos os visinhos do vértice corrente forem visitados, marque-o como visitado, o que fará com que ele não seja mais analisado (sua distância é mínima e final). 5. Eleja o vértice não visitado com a menor distância (a partir do vértice inicial) como o vértice corrente e continue a partir do passo 3. 47 Exemplo: Algoritmo de Dijkstra Para o exemplo a seguir, o vértice 1 é o vértice origem e o vértice corrente do início do algoritmo. A distância dele para todos os outros vértices é mostrada em vermelho. A distância para seu vizinho (vértice 2) é igual a 1. D = 1D = ∞ D = ∞ D = ∞ D = 0 48 Exemplo: Algoritmo de Dijkstra O vértice 2 passa a ser o vértice corrente. A partir dele, atinge-se os vértices 3 e 5, sendo as respectivas distâncias calculadas para 2 (ao invés de infinito) e 2. D = 1 D = 2 D = 2 D = ∞ D = 0 D = 1 49 Exemplo: Algoritmo de Dijkstra O vértice 3 é o vértice corrente. A partir dele alcança-se o vértice 4, com distância 4. Neste ponto todos os vértices foram visitados e o vetor de distâncias mínimas a partir do vértice 1 é D = [0 1 2 4 2]. D = 1 D = 2 D = 2 D = 4 D = 0 D = 2 50 Pseudocódigo: Algoritmo de Dijkstra Dijkstra(grafo,origem) para cada vértice v em grafo distancia[v] = Infinito; predecessor[v] = -1; fim distancia[origem] = 0; Q = todos os vértices de grafo; enquanto (Q não é vazio) faça, u = vértice é grafo com menor distância; se (distancia[u] == Infinito) Salta o laço; fim remova u de Q; para cada vizinho v de u d = distancia[u] + distancia entre u e v; se (d < distancia[v]), distancia[v] = d; predecessor[v] = u; fim fim fim fim 51 Caminhos mínimos entre todos os pares de vértices O Algoritmo de Floyd-Warshall é um algoritmo muito eficiente para encontrar todos as distâncias mínimas dos pares de caminhos em um grafo. Em aplicações onde é necessária a criação de uma matriz de caminhos mínimos ao invés de um vetor, este algoritmo é mais aconselhável. 52 Algoritmo de Floid-Warshall O algoritmo de Floyd-Warshall deve primeiramente modificar a matriz de adjacência do grafo, de modo que todas as posições dos pares de vértices em que não houver conexões sejam setadas como Infinito. Um pseudocódigo da implementação é mostrado a seguir, sendo a entrada do programa a própria matriz de adjacências. O programa retorna uma matriz com todos os pares de caminhos mínimos. 53 Pseudocódigo: Algoritmo de Floyd-Warshall FloidWarshall(Madj) para cada linha i em Madj para cada coluna j em Madj se (Madf[i][j] == 0) Madj[i][j] == Infinito; fim fim fim t = numero de linhas de Madj; Cmin = Madj; para k igual a 1 até t, para i igual a 1 até t, para j igual a 1 até t, Cmin[i][j] = min(Cmin[i][j],Cmin[i][k]+Cmin[k][j]); fim fim fim retorna Cmin; fim 54 Referências Bibliográficas T. H. Cormen, C. H. Leiserson, R. L. Rivest, C. Stein, ”Algoritmos: Teoria e Prática”, Editora Campus, 2002. R. Sedgewick, “Algorithms in C”, Addison Wisley, 1990. http://www.ime.usp.br/~pf/algoritmos_para_grafos/ http://www.ime.usp.br/~pf/analise_de_algoritmos/lectures.html http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm http://www.algorithmist.com/index.php/Floyd-Warshall%27s_Algorithm 55 Exercícios Propostos 1. Um grafo não-ponderado é representado pela lista de adjacências Adj = {[2]; [3]; [4,5,8]; []; [6,7]; [1]; [9]; [10]; [10]; []}. Represente graficamente este grafo e aplique o algoritmo de busca em largura e busca em profundidade para encontrar o caminho entre os vértices 1 e 10. 2. Considere um grafo orientado e ponderado que seja representado pela matriz de adjacência abaixo. Simule o algoritmo de Dijkstra e determine o vetor de distâncias a partir do vértice 1. Faça gráficos para mostrar a evolução do algoritmo. 3. Usando a mesma matriz de adjacências do exercício anterior, implemente o algoritmo de Floid-Warshall em qualquer linguagem para calcular a matriz de caminhos mínimos. 56 Atividade extra Uma rede de informação possui uma topologia representada por um grafo cuja matriz de adjacências é dada a seguir. Após um certo tempo de operação t o roteador representado pelo vértice 2 cai, deixando de existir as conexões que chegam e saem deste vértice. Passado um tempo t após a queda do vértice 2 o vértice 6 também cai e após 2t é a vez do vértice 12 sair de operação. Utilizando recursos computacionais, faça uma análise dos custos representados para o escoamento de informação devido à inoperabilidade destes roteadores e justifique suas afirmações por meio de gráficos e tabelas. Qual destas falhas representa a principal falha individual?
Compartilhar