CES11 ALGORITMOS E ESTRUTURAS DE DADOS Captulo VII

  • Slides: 89
Download presentation
CES-11 ALGORITMOS E ESTRUTURAS DE DADOS Capítulo VII Algoritmos para Grafos

CES-11 ALGORITMOS E ESTRUTURAS DE DADOS Capítulo VII Algoritmos para Grafos

Capítulo VII – Algoritmos para Grafos 7. 1 – Caminhos de menores custos 7.

Capítulo VII – Algoritmos para Grafos 7. 1 – Caminhos de menores custos 7. 2 – Busca em profundidade para digrafos 7. 3 – Teste de aciclicidade 7. 4 – Ordenação topológica 7. 5 – Caminhos críticos em digrafos acíclicos 7. 6 – Componentes fortemente conexos 7. 7 – Árvores de cobertura de custo mínimo 7. 8 – Travessia de grafos não-orientados 7. 9 – Pontos de articulação e componentes biconexos

7. 1 – Caminhos de Menores Custos 7. 1. 1 – O problema n

7. 1 – Caminhos de Menores Custos 7. 1. 1 – O problema n Seja um digrafo G = {V, A} em que cada arco de A possui uma informação inteira positiva chamada custo n Seja v um vértice de V que recebe o nome de fonte n Determinar o caminho mais barato e seu custo da fonte para todos os outros vértices de V v (fonte)

n Custo de um caminho: soma dos custos dos arcos desse caminho Caminhos de

n Custo de um caminho: soma dos custos dos arcos desse caminho Caminhos de 1 a 3: 1 -2 -3 (custo 60) 1 -4 -3 (custo 50) Caminhos de 1 a 5: 1 -5 (custo 100) 1 -2 -3 -5 (custo 70) 1 -4 -5 (custo 90) 1 -4 -3 -5 (custo 60) v (fonte)

7. 1. 2 – Uma solução: algoritmo de Dijkstra n O digrafo é G

7. 1. 2 – Uma solução: algoritmo de Dijkstra n O digrafo é G = {V, A} n O conjunto de vértices é V = {1, 2, . . . , n} Esse algoritmo e o conceito de custos podem ser aplicados n C(i, j): custo associado ao arco i j em grafos nãoorientados n A fonte é o vértice 1 n Se o arco i j não existe, C(i, j) = v (fonte)

Vetor D: destinado a guardar os caminhos mais baratos e seus custos n No

Vetor D: destinado a guardar os caminhos mais baratos e seus custos n No início, D[i] guarda: Caminho [1 -i] e C(1, i) Caminho Custo D[2] 1 -2 10 Vetor D D[3] D[4] 1 -3 1 -4 ∞ 30 D[5] 1 -5 100 Definitivo não não v (fonte) não

Caminho Custo D[2] 1 -2 10 Vetor D D[3] D[4] 1 -3 1 -4

Caminho Custo D[2] 1 -2 10 Vetor D D[3] D[4] 1 -3 1 -4 ∞ 30 D[5] 1 -5 100 Definitivo não não Simulação: Menor custo: 10 D[2] é definitivo não

Caminho Custo D[2] 1 -2 10 Vetor D D[3] D[4] 1 -3 1 -4

Caminho Custo D[2] 1 -2 10 Vetor D D[3] D[4] 1 -3 1 -4 ∞ 30 D[5] 1 -5 100 Definitivo sim não Menor custo: 10 D[2] é definitivo não

Caminho Custo D[2] 1 -2 10 Vetor D D[3] D[4] 1 -3 1 -4

Caminho Custo D[2] 1 -2 10 Vetor D D[3] D[4] 1 -3 1 -4 ∞ 30 D[5] 1 -5 100 Definitivo sim não Novos caminhos: Custo (1 -3) = ∞ Custo (1 -2 -3) = Custo (1 -2) + C(2, 3) = 10 + 50 = 60 (menor) Custo (1 -4) = 30 (menor) Custo (1 -2 -4) = Custo (1 -2) + C(2, 4) = 10 + ∞ = ∞ Custo (1 -5) = 100 (menor) Custo (1 -2 -5) = Custo (1 -2) + C(2, 5) = 10 + ∞ = ∞ não

Caminho Custo D[2] 1 -2 10 Definitivo sim Novos caminhos: Custo (1 -3) =

Caminho Custo D[2] 1 -2 10 Definitivo sim Novos caminhos: Custo (1 -3) = ∞ Custo (1 -2 -3) = Custo (1 -2) + C(2, 3) = 10 + 50 = 60 (menor) Custo (1 -4) = 30 (menor) Custo (1 -2 -4) = Custo (1 -2) + C(2, 4) = 10 + ∞ = ∞ Custo (1 -5) = 100 (menor) Custo (1 -2 -5) = Custo (1 -2) + C(2, 5) = 10 + ∞ = ∞ Vetor D D[3] D[4] 1 -2 -3 1 -4 60 30 não D[5] 1 -5 100 não

Caminho Custo D[2] 1 -2 10 Definitivo sim Menor custo: 30 D[4] é definitivo

Caminho Custo D[2] 1 -2 10 Definitivo sim Menor custo: 30 D[4] é definitivo Vetor D D[3] D[4] 1 -2 -3 1 -4 60 30 não D[5] 1 -5 100 não

Caminho Custo D[2] 1 -2 10 Definitivo sim Menor custo: 30 D[4] é definitivo

Caminho Custo D[2] 1 -2 10 Definitivo sim Menor custo: 30 D[4] é definitivo Vetor D D[3] D[4] 1 -2 -3 1 -4 60 30 não sim D[5] 1 -5 100 não

Caminho Custo D[2] 1 -2 10 Definitivo sim Novos caminhos: Custo (1 -2 -3)

Caminho Custo D[2] 1 -2 10 Definitivo sim Novos caminhos: Custo (1 -2 -3) = 60 Custo (1 -4 -3) = Custo (1 -4) + C(4, 3) = 30 + 20 = 50 (menor) Custo (1 -5) = 100 Custo (1 -4 -5) = Custo (1 -4) + C(4, 5) = 30 + 60 = 90 (menor) Vetor D D[3] D[4] 1 -2 -3 1 -4 60 30 não sim D[5] 1 -5 100 não

Caminho Custo D[2] 1 -2 10 Definitivo sim Novos caminhos: Custo (1 -2 -3)

Caminho Custo D[2] 1 -2 10 Definitivo sim Novos caminhos: Custo (1 -2 -3) = 60 Custo (1 -4 -3) = Custo (1 -4) + C(4, 3) = 30 + 20 = 50 (menor) Custo (1 -5) = 100 Custo (1 -4 -5) = Custo (1 -4) + C(4, 5) = 30 + 60 = 90 (menor) Vetor D D[3] D[4] 1 -4 -3 1 -4 50 30 não sim D[5] 1 -4 -5 90 não

Caminho Custo D[2] 1 -2 10 Definitivo sim Menor custo: 50 D[3] é definitivo

Caminho Custo D[2] 1 -2 10 Definitivo sim Menor custo: 50 D[3] é definitivo Vetor D D[3] D[4] 1 -4 -3 1 -4 50 30 não sim D[5] 1 -4 -5 90 não

Caminho Custo D[2] 1 -2 10 Definitivo sim Menor custo: 50 D[3] é definitivo

Caminho Custo D[2] 1 -2 10 Definitivo sim Menor custo: 50 D[3] é definitivo Vetor D D[3] D[4] 1 -4 -3 1 -4 50 30 sim D[5] 1 -4 -5 90 não

Caminho Custo D[2] 1 -2 10 Definitivo sim Novos caminhos: Custo (1 -4 -5)

Caminho Custo D[2] 1 -2 10 Definitivo sim Novos caminhos: Custo (1 -4 -5) = 90 Custo (1 -4 -3 -5) = Custo (1 -4 -3) + C(3, 5) = 50 + 10 = 60 (menor) Vetor D D[3] D[4] 1 -4 -3 1 -4 50 30 sim D[5] 1 -4 -5 90 não

Caminho Custo D[2] 1 -2 10 Definitivo sim Novos caminhos: Custo (1 -4 -5)

Caminho Custo D[2] 1 -2 10 Definitivo sim Novos caminhos: Custo (1 -4 -5) = 90 Custo (1 -4 -3 -5) = Custo (1 -4 -3) + C(3, 5) = 50 + 10 = 60 (menor) Vetor D D[3] D[4] 1 -4 -3 1 -4 50 30 sim D[5] 1 -4 -3 -5 60 não

Caminho Custo D[2] 1 -2 10 Definitivo sim Menor custo: 60 D[5] é definitivo

Caminho Custo D[2] 1 -2 10 Definitivo sim Menor custo: 60 D[5] é definitivo Vetor D D[3] D[4] 1 -4 -3 1 -4 50 30 sim D[5] 1 -4 -3 -5 60 não

Caminho Custo D[2] 1 -2 10 Definitivo sim Menor custo: 60 D[5] é definitivo

Caminho Custo D[2] 1 -2 10 Definitivo sim Menor custo: 60 D[5] é definitivo Vetor D D[3] D[4] 1 -4 -3 1 -4 50 30 sim D[5] 1 -4 -3 -5 60 sim

D 7. 1. 3 – Programação Vetor do algoritmo de Dijkstra Caminho D[2] 1

D 7. 1. 3 – Programação Vetor do algoritmo de Dijkstra Caminho D[2] 1 -2 D[3] 1 -4 -3 D[4] 1 -4 D[5] 1 -4 -3 -5 Custo 10 50 30 60 Definitivo Sim sim sim Declarações para o vetor D: struct infovert { int custo; logic definitivo; caminho cam; }; infovert *D; Caminho é lista de vértices: typedef noh *posicao; struct noh { vertice elem; posicao prox; }; struct caminho { posicao inic, fim; };

Vetor D D[3] D[4] 1 -4 -3 1 -4 Caminho D[2] 1 -2 Custo

Vetor D D[3] D[4] 1 -4 -3 1 -4 Caminho D[2] 1 -2 Custo 10 50 30 60 Definitivo Sim sim sim Caminho é lista de vértices: typedef noh *posicao; struct noh { vertice elem; posicao prox; }; struct caminho { posicao inic, fim; }; D[5] 1 -4 -3 -5 Caminho – operadores: void Inic. Caminho (caminho *); posicao Primeira (caminho); posicao Proxima (posicao); posicao Fim (caminho); void Inserir. No. Final (int, caminho *); int Elemento (posicao, caminho); void Escrever. Caminho (caminho); void Copiar. Caminho (caminho *, caminho); void Esvaziar. Caminho (caminho *);

Algoritmo de Dijkstra: void Dijkstra (Grafo *G) { infovert *D; int i, novocusto; celulaadj

Algoritmo de Dijkstra: void Dijkstra (Grafo *G) { infovert *D; int i, novocusto; celulaadj *p; vertice w, v; Inicializar vetor D; Calcular vetor D definitivo; Escrever vetor D; }

Inicializar vetor D: D = malloc ((G->nvert + 1) * sizeof(infovert)); for (i =

Inicializar vetor D: D = malloc ((G->nvert + 1) * sizeof(infovert)); for (i = 2; i <= G->nvert; i++) { D[i]. custo = infinito; D[i]. definitivo = FALSE; Inic. Caminho (&D[i]. cam); Inserir. No. Final (1, &D[i]. cam); Inserir. No. Final (i, &D[i]. cam); } for (p = G->Vertices[1]; p != NULL; p = p->prox) D[p->vert]. custo = p->custo;

Calcular vetor D definitivo: for (i = 2; i <= G->nvert; i++) { w

Calcular vetor D definitivo: for (i = 2; i <= G->nvert; i++) { w = Minimo (D, G->nvert); D[w]. definitivo = TRUE; for (v = 2; v <= G->nvert; v++) if (D[v]. definitivo == FALSE) { novocusto = D[w]. custo + Custo. Arco (w, v, G); if (novocusto < D[v]. custo) { D[v]. custo = novocusto; Copiar. Caminho (&D[v]. cam, D[w]. cam); Inserir. No. Final (v, &D[v]. cam); } } }

Escrever vetor D: for (i = 2; i <= G->nvert; i++) { write ("Caminho

Escrever vetor D: for (i = 2; i <= G->nvert; i++) { write ("Caminho (1, ", i, "): "); write ("Custo = ", D[i]. custo, "; Vertices: "); Escrever. Caminho (D[i]. cam); } Saída para o grafo acima: Caminhos mais baratos ao vertice 1: Caminho (1, (1, 2): 3): 4): 5): Custo = = 10; 50; 30; 60; Vertices: 1 1 2 4 4 4 3 3 5

n Algoritmo de Floyd-Warshall: Determina o caminho mais barato entre todos os pares de

n Algoritmo de Floyd-Warshall: Determina o caminho mais barato entre todos os pares de vértices de um grafo n Aplicações de caminhos de menores custos: - Escolha da melhor rota para transitar numa malha rodoviária ou para viagens aéreas (GPS) - Determinação do caminho de menor resistência em circuitos elétricos - Otimização da locação de torres de transmissão de energia elétrica

Capítulo VII – Algoritmos para Grafos 7. 1 – Caminhos de menores custos 7.

Capítulo VII – Algoritmos para Grafos 7. 1 – Caminhos de menores custos 7. 2 – Busca em profundidade para digrafos 7. 3 – Teste de aciclicidade 7. 4 – Ordenação topológica 7. 5 – Caminhos críticos em digrafos acíclicos 7. 6 – Componentes fortemente conexos 7. 7 – Árvores de cobertura de custo mínimo 7. 8 – Travessia de grafos não-orientados 7. 9 – Pontos de articulação e componentes biconexos

7. 2 – Busca em Profundidade para Digrafos 7. 2. 1 – Conceito e

7. 2 – Busca em Profundidade para Digrafos 7. 2. 1 – Conceito e metodologia n Busca em profundidade é um método de travessia de grafos (orientados ou não) n É esqueleto para a solução de muitos problemas relacionados com grafos (orientados ou não)

n Travessia de grafos: visitação sistemática de todos os vértices de um grafo, usando

n Travessia de grafos: visitação sistemática de todos os vértices de um grafo, usando os arcos para transitar - Busca em profundidade: generalização de pré, pós e ordem central para árvores - Busca em largura: generalização da ordem de nível para árvores (será apresentada para os grafos não-orientados)

Metodologia para digrafos: Seja G um digrafo 1. Assinalar todos os vértices de G

Metodologia para digrafos: Seja G um digrafo 1. Assinalar todos os vértices de G como não-visitados 2. Seja v um vértice de G selecionado como vértice inicial; Marcar v como visitado 3. Visitar cada vértice w adjacente a v, não visitado 4. Ao visitar um vértice w, marcá-lo como visitado e visitar cada vértice não visitado, adjacente a w (recursividade) 5. A busca em v é encerrada quando todos os vértices w adjacentes a v tiverem sido visitados 6. Se alguns vértices permanecerem não visitados,

Exemplo: seja o digrafo: n O esquema de visitação é mostrado a seguir

Exemplo: seja o digrafo: n O esquema de visitação é mostrado a seguir

Números de visita: ao lado dos círculos, mostram a ordem de visitação Linhas tracejadas:

Números de visita: ao lado dos círculos, mostram a ordem de visitação Linhas tracejadas: tentativas de

7. 2. 2 – Programação e estrutura de dados Serão usadas as listas de

7. 2. 2 – Programação e estrutura de dados Serão usadas as listas de adjacências

struct celulaadj { vertice vert; celulaadj *prox; }; typedef celulaadj *listadj; struct celulavert {

struct celulaadj { vertice vert; celulaadj *prox; }; typedef celulaadj *listadj; struct celulavert { logic visit; int nvis; listadj adjs; }; typedef int vertice; struct Grafo { int nvert; celulavert *Vertices; }; Grafo G; int cont;

void Travessia (Grafo *G) { vertice v; for (v = 1; v <= G->nvert;

void Travessia (Grafo *G) { vertice v; for (v = 1; v <= G->nvert; v++) G->Vertices[v]. visit = FALSE; cont = 0; for (v = 1; v <= G->nvert; v++) if (G->Vertices[v]. visit == FALSE) Busca. Prof (v, G); } A passagem de argumento por referência evita a necessidade de cópia do grafo dentro da função A execução de uma chamada de Busca. Prof pode marcar diversos vértices como visitados

void Busca. Prof (vertice v, Grafo *G) { celulaadj *p; G->Vertices[v]. visit = TRUE;

void Busca. Prof (vertice v, Grafo *G) { celulaadj *p; G->Vertices[v]. visit = TRUE; cont++; G->Vertices[v]. nvis = cont; p = G->Vertices[v]. adjs; while (p != NULL) { if (G->Vertices[p->vert]. visit == FALSE) Busca. Prof(p->vert, G); p = p->prox; } } A execução da chamada de Busca. Prof pode marcar diversos vértices com visitados

7. 2. 3 – Floresta da busca em profundidade Os arcos com linha cheia

7. 2. 3 – Floresta da busca em profundidade Os arcos com linha cheia levam a vértices não-visitados São chamados arcos de árvore Formam a floresta da busca em profundidade

n Outros tipos de arcos (linha tracejada): Arcos de volta, arcos para frente e

n Outros tipos de arcos (linha tracejada): Arcos de volta, arcos para frente e arcos cruzantes Arcos de volta: vão de um vértice a um seu ancestral na floresta Um arco de um vértice para si mesmo é um arco de volta Exemplos: 5 8, 9 2, 12 10

Arcos para frente: arcos não de árvore, que vão de um vértice a um

Arcos para frente: arcos não de árvore, que vão de um vértice a um seu descendente próprio Exemplos: 1 8, 1 5, 3 7

Arcos cruzantes: vão de um vértice a outro que não seja seu ancestral nem

Arcos cruzantes: vão de um vértice a outro que não seja seu ancestral nem descendente na floresta Vão sempre da direita para a esquerda Exemplos: 8 7, 11 6, 14 11, 13 6, 11 2, 10 9

Características: se v w é n Arco de árvore ou arco para frente: nvis

Características: se v w é n Arco de árvore ou arco para frente: nvis (v) < nvis (w) n Arco de volta: nvis (v) nvis (w) n Arco cruzante: nvis (v) > nvis (w) Tal floresta e tal classificação de arcos são usados para resolver muitos problemas sobre grafos

Capítulo VII – Algoritmos para Grafos 7. 1 – Caminhos de menores custos 7.

Capítulo VII – Algoritmos para Grafos 7. 1 – Caminhos de menores custos 7. 2 – Busca em profundidade para digrafos 7. 3 – Teste de aciclicidade 7. 4 – Ordenação topológica 7. 5 – Caminhos críticos em digrafos acíclicos 7. 6 – Componentes fortemente conexos 7. 7 – Árvores de cobertura de custo mínimo 7. 8 – Travessia de grafos não-orientados 7. 9 – Pontos de articulação e componentes biconexos

7. 3 – Teste de Aciclicidade 7. 3. 1 – Digrafos acíclicos (dga’s) n

7. 3 – Teste de Aciclicidade 7. 3. 1 – Digrafos acíclicos (dga’s) n Dga é um digrafo sem ciclos n Dga é um caso particular de digrafos e árvore é um caso particular de dga’s n Certas aplicações exigem que o digrafo seja acíclico n É necessário verificar se o digrafo lido é um dga, para garantir a consistência dos dados de entrada

n Ponto de partida em um dga: vértice não-adjacente de nenhum outro vértice n

n Ponto de partida em um dga: vértice não-adjacente de nenhum outro vértice n Ponto final em um dga: vértice sem adjacentes n Um dga tem um ou mais pontos de partida e um ou mais pontos finais n Exemplo: Rede PERT-CPM Uma tarefa não pode começar enquanto aquelas das quais ela depende não Pontos de partida Pontos finais

n Exemplo: expressões aritméticas com subexpressões repetidas Seja a expressão: ((a+b)*c repetiçõe s +

n Exemplo: expressões aritméticas com subexpressões repetidas Seja a expressão: ((a+b)*c repetiçõe s + ((a+b)+e ) * (e+f)) * ((a+b)*c) repetiçõe s Dga da expressão No dga não há repetições

n Exemplo: Seja Conj = {1, 2, 3} e seja P(Conj) o conjunto potência

n Exemplo: Seja Conj = {1, 2, 3} e seja P(Conj) o conjunto potência de Conj P(Conj) = { , {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, {1, 2, 3}} Seja a relação ω = (Contém e tem exatamente 1 elemento a mais que), relação essa definida em P(Conj) e ω podem ser representados pelo seguinte dga:

7. 3. 2 – Implementação do teste de aciclicidade n Pode-se usar a busca

7. 3. 2 – Implementação do teste de aciclicidade n Pode-se usar a busca em profundidade como esqueleto para sua solução n Se um arco de volta for encontrado durante a busca, então o grafo não é acíclico

Método: n Manter uma pilha com todos os ancestrais do vértice que está sendo

Método: n Manter uma pilha com todos os ancestrais do vértice que está sendo visitado, incluindo ele próprio n Partindo dele, para visitar seus adjacentes, se um deles, já visitado, pertencer a tal pilha, então existe o arco de volta e o grafo não é acíclico n Quando todos os seus adjacentes já tiverem sido visitados, deve-se retirá-lo da pilha de ancestrais

Exemplo: n Ao visitar o vértice 5, estarão na pilha seus ancestrais 1, 3,

Exemplo: n Ao visitar o vértice 5, estarão na pilha seus ancestrais 1, 3, 8, 2 e 5 n Adjacentes de 5: 8 e 9 n Na tentativa de visitar o 8, encontra-se o arco de volta n O digrafo não é um dga n A seguir um algoritmo para o teste de aciclicidade

/* Variaveis globais */ int cont; pilha P; logic aciclico; void Travessia (Grafo *G)

/* Variaveis globais */ int cont; pilha P; logic aciclico; void Travessia (Grafo *G) { vertice v; Inic. Pilha (&P); aciclico = TRUE; for (v = 1; v <= G->nvert; v++) G->Vertices[v]. visit = FALSE; cont = 0; for (v = 1; v <= G->nvert && aciclico; v++) if (G->Vertices[v]. visit == FALSE) Busca. Prof (v, G); } O algoritmo usa como esqueleto o algoritmo da busca em profundidade (em amarelo os enxertos)

void Busca. Prof (vertice v, Grafo *G) { celulaadj *p; G->Vertices[v]. visit = TRUE;

void Busca. Prof (vertice v, Grafo *G) { celulaadj *p; G->Vertices[v]. visit = TRUE; cont++; G->Vertices[v]. nvis = cont; Empilhar (v, &P); p = G->Vertices[v]. adjs; while (p != NULL && aciclico) { if (G->Vertices[p->vert]. visit == FALSE) Busca. Prof(p->vert, G); else if (Procurar (p->vert, P) == TRUE) aciclico = FALSE; p = p->prox; } Desempilhar (&P); }

Capítulo VII – Algoritmos para Grafos 7. 1 – Caminhos de menores custos 7.

Capítulo VII – Algoritmos para Grafos 7. 1 – Caminhos de menores custos 7. 2 – Busca em profundidade para digrafos 7. 3 – Teste de aciclicidade 7. 4 – Ordenação topológica 7. 5 – Caminhos críticos em digrafos acíclicos 7. 6 – Componentes fortemente conexos 7. 7 – Árvores de cobertura de custo mínimo 7. 8 – Travessia de grafos não-orientados 7. 9 – Pontos de articulação e componentes biconexos

7. 4 – Ordenação Topológica n Um grande projeto costuma ser dividido numa coleção

7. 4 – Ordenação Topológica n Um grande projeto costuma ser dividido numa coleção de tarefas, algumas das quais são prérequisitos de outras n Exemplo: disciplinas D 1, D 2 , D 3, D 4 e D 5 de um curso

n Ordenação topológica: processo de ordenar linearmente os vértices de um dga, de forma

n Ordenação topológica: processo de ordenar linearmente os vértices de um dga, de forma que - n Se há um arco do vértice i para o vértice j, então i aparece antes de j na ordenação Exemplo: seja o dga visto anteriormente (D 1, D 2, D 3, D 4, D 5) e (D 2, D 4, D 1, D 3, D 5) são duas de suas ordenações topológicas

n Essa ordenação pode ser feita, usando como esqueleto a busca em profundidade n

n Essa ordenação pode ser feita, usando como esqueleto a busca em profundidade n No final da chamada da rotina Busca. Prof, empilha-se o vértice argumento n No final da rotina Travessia, enquanto houver elementos na pilha, retira-se o vértice do topo da pilha, imprimindo-o

n Exemplo: seja um dga com numeração aleatória dos vértices: Opta-se por iniciar uma

n Exemplo: seja um dga com numeração aleatória dos vértices: Opta-se por iniciar uma busca pelo vértice de menor número Ordenações topológicas diferentes podem ser obtidas, variando-se opção pelo vértice inicial

Floresta da busca em profundidade (só com os arcos de árvore): Esta é uma

Floresta da busca em profundidade (só com os arcos de árvore): Esta é uma das muitas possíveis ordenações topológicas Se apenas uma tarefa puder ser executada por vez, esta ordenação garante a correta execução

n Ordenação topológica ajuda estabelecer uma ordem de execução das tarefas de um grande

n Ordenação topológica ajuda estabelecer uma ordem de execução das tarefas de um grande projeto n Isso é crítico quando apenas um pequeno número de tarefas pode ser executado simultaneamente n É o caso de um curso com módulos semestrais, com cinco ou seis disciplinas por módulos n Faz-se um grafo de pré-requisitos das disciplinas e uma ordenação topológica mais sofisticada, para obter a simultaneidade requerida

Capítulo VII – Algoritmos para Grafos 7. 1 – Caminhos de menores custos 7.

Capítulo VII – Algoritmos para Grafos 7. 1 – Caminhos de menores custos 7. 2 – Busca em profundidade para digrafos 7. 3 – Teste de aciclicidade 7. 4 – Ordenação topológica 7. 5 – Caminhos críticos em digrafos acíclicos 7. 6 – Componentes fortemente conexos 7. 7 – Árvores de cobertura de custo mínimo 7. 8 – Travessia de grafos não-orientados 7. 9 – Pontos de articulação e componentes biconexos

7. 5 – Caminhos Críticos em Digrafos Acíclicos 7. 5. 1 – Conceitos e

7. 5 – Caminhos Críticos em Digrafos Acíclicos 7. 5. 1 – Conceitos e problemas n Seja o seguinte dga: Os vértices são tarefas de um A duração de cada tarefa aparece ao lado Num arco, o destino não começa antes do

Problemas: n Qual o tempo mínimo para que o projeto seja executado? n Quais

Problemas: n Qual o tempo mínimo para que o projeto seja executado? n Quais as tarefas que não podem sofrer qualquer aumento no tempo de execução, para que esse tempo mínimo não aumente? n A sequência formada pelas referidas tarefas é chamada de caminho crítico (pode haver mais de um)

7. 5. 2 – Metodologia n É útil calcular o tempo mínimo de término

7. 5. 2 – Metodologia n É útil calcular o tempo mínimo de término (TMT) de cada vértice n O tempo mínimo procurado é o maior de todos os TMT’s das tarefas finais

n TMT’s das tarefas de partida 1, 2 e 3 são imediatos: - Correspondem

n TMT’s das tarefas de partida 1, 2 e 3 são imediatos: - Correspondem à duração delas: 8, 10 e 6 8 10 6

n O TMT das tarefas dependentes apenas de 1, 2 e 3 já pode

n O TMT das tarefas dependentes apenas de 1, 2 e 3 já pode ser calculado: - TMT (4) = max (TMT (1), TMT (2)) + Duração (4) = 25 TMT (5) = max (TMT (2), TMT (3)) + Duração (5) = 22 8 10 25 6 22

n Assim prosseguindo: - TMT (6) = TMT (4) + Duração (6) = 34

n Assim prosseguindo: - TMT (6) = TMT (4) + Duração (6) = 34 TMT (7) = max (TMT (2), TMT (5)) + Duração (7) = 30 TMT (8) = TMT (5) + Duração (8) = 32 8 10 25 34 6 22 30 32

n Assim prosseguindo: - TMT (9) = TMT (7) + Duração (9) = 45

n Assim prosseguindo: - TMT (9) = TMT (7) + Duração (9) = 45 TMT (10) = max (TMT (5), TMT (7))+Duração (10) = 38 8 10 25 34 6 22 30 45 32 38

n Finalmente: - TMT (11) = max (TMT (4), TMT (9))+Duração (11) = 58

n Finalmente: - TMT (11) = max (TMT (4), TMT (9))+Duração (11) = 58 TMT (12) = max (TMT (7), TMT (10))+Duração (12) = 53 8 10 25 34 22 30 45 58 6 32 38 53

n Tarefas finais: 6, 8, 11 e 12 - TMT (Projeto) = = max

n Tarefas finais: 6, 8, 11 e 12 - TMT (Projeto) = = max (TMT (6), TMT (8), TMT (11), TMT (12)) = max (34, 32, 58, 53) = 58 8 10 25 34 22 30 45 58 6 32 38 53

7. 5. 3 – Algoritmo recursivo para o cálculo do TMT do projeto n

7. 5. 3 – Algoritmo recursivo para o cálculo do TMT do projeto n Primeiramente introduz-se uma tarefa final artificial TArtif de duração zero (um arco de todos os pontos finais para ela):

n Seja T uma tarefa (índice no vetor de vértices) n Cada posição nesse

n Seja T uma tarefa (índice no vetor de vértices) n Cada posição nesse vetor pode ter pelo menos 2 campos: - n duracao: duração da tarefa tmt: TMT da tarefa (a ser calculado pelo algoritmo) O algoritmo Termino (T) a seguir retorna o TMT de T, armazenando-o antes no vetor de vértices O tempo mínimo para a execução do projeto é calculado invocando: Termino (TArtif)

Fundamento recursivo do algoritmo Termino (T): n Se T for um ponto de partida,

Fundamento recursivo do algoritmo Termino (T): n Se T for um ponto de partida, Termino (T) = T. duracao n Senão Termino (T) = T. duracao + Max (todos os X. tmt), onde X é uma tarefa da qual T é adjacente n Se, para alguma tarefa X, X. tmt não tiver sido calculada, X. tmt = Termino (X) Pode ser que, para alguma tarefa X, X. tmt já tenha sido calculada

Algoritmo: Se T não for adjacente de nenhuma tarefa: int Termino (Tarefa T) {

Algoritmo: Se T não for adjacente de nenhuma tarefa: int Termino (Tarefa T) { maior = 0 int maior, aux; Tarefa: X; T. tmt = T. duracao maior = 0; para (cada tarefa X da qual T é adjacente) { se (X. tmt não foi calculado) aux = Termino (X); Este algoritmo senão aux = X. tmt; precisa ser se (aux > maior) maior = aux; adaptado à } estrutura de T. tmt = maior + T. duracao; dados para o grafo return T. tmt; } É útil criar para cada tarefa, uma lista das tarefas das quais ela é adjacente, além da lista das adjacentes a ela (grafo reverso)

7. 5. 4 – Determinação das tarefas do caminho crítico n A primeira delas

7. 5. 4 – Determinação das tarefas do caminho crítico n A primeira delas é a de maior TMT entre as finais, excluindo Tartif (pode haver mais de uma) n Para cada tarefa T acrescentada ao caminho crítico, a próxima é aquela de maior TMT dentre as que T é adjacente (pode haver mais de uma) n A última a ser acrescentada é uma que não é adjacente de nenhuma outra (pode haver mais de uma)

Exemplo: caminho crítico do grafo ilustrativo 8 10 25 34 22 30 45 58

Exemplo: caminho crítico do grafo ilustrativo 8 10 25 34 22 30 45 58 6 32 38 53

Capítulo VII – Algoritmos para Grafos 7. 1 – Caminhos de menores custos 7.

Capítulo VII – Algoritmos para Grafos 7. 1 – Caminhos de menores custos 7. 2 – Busca em profundidade para digrafos 7. 3 – Teste de aciclicidade 7. 4 – Ordenação topológica 7. 5 – Caminhos críticos em digrafos acíclicos 7. 6 – Componentes fortemente conexos 7. 7 – Árvores de cobertura de custo mínimo 7. 8 – Travessia de grafos não-orientados 7. 9 – Pontos de articulação e componentes biconexos

7. 6 – Componentes Fortemente Conexos 7. 6. 1 – Conceito n n Componente

7. 6 – Componentes Fortemente Conexos 7. 6. 1 – Conceito n n Componente fortemente conexo (CFC) de um digrafo G = {V, A} é um sub-grafo de G contendo: - Um conjunto máximo de vértices de V, no qual há um caminho de qualquer desses vértices para qualquer outro do conjunto - Todos os arcos de A que ligam somente vértices desse conjunto Um CFC pode ser composto de apenas um vértice

Exemplo: seja G = {V, A} o seguinte digrafo: Seja o seguinte sub-grafo G’

Exemplo: seja G = {V, A} o seguinte digrafo: Seja o seguinte sub-grafo G’ de G: Será que G’ é um CFC de G? n n Há caminho de 4 para 1, 2 e 3 e também desses para 4 G’ = {{1, 2, 3}, {1 2, 2 3, 3 1}} não é um CFC de G, pois {1, 2, 3} não é máximo

Exemplo: seja G = {V, A} o seguinte digrafo: Qualquer vértice de G está

Exemplo: seja G = {V, A} o seguinte digrafo: Qualquer vértice de G está em algum e em um só de seus CFC’s de G: Alguns arcos de G podem não estar em nenhum de seus CFC’s: Tais arcos são chamados arcos que cruzam componentes

Digrafo reduzido de um digrafo é aquele: n n Em que cada vértice é

Digrafo reduzido de um digrafo é aquele: n n Em que cada vértice é o conjunto de vértices de um dos CFC’s desse digrafo Cujos arcos são unificações de seus arcos que cruzam componentes Digrafos reduzidos são sempre acíclicos G CFC’s de G Digrafo reduzido de G

7. 6. 2 – Aplicações de CFC’s n Divisão de problemas sobre digrafos em

7. 6. 2 – Aplicações de CFC’s n Divisão de problemas sobre digrafos em subproblemas, um para cada CFC n Estudo de privacidade em sistemas de comunicação n Análise do fluxo de controle para a validação de programas n Computer-aided design (CAD) n Análise de circuitos eletrônicos – classes de equivalência n Paralelização de laços sequenciais

Exemplo: Paralelização de laços sequenciais n Seja o seguinte laço em C e o

Exemplo: Paralelização de laços sequenciais n Seja o seguinte laço em C e o grafo de dependências entre seus comandos: C 1: C 2: C 3: C 4: C 5: C 6: for (i = 1; i <= n; i++) { A[i] = B[i] + C[i]; D[i] = E[i] + F[i]; E[i+1] = A[i] + G[i]; H[i] = F[i] + B[i]; B[i+1] = E[i+1] + M[i]; F[i+1] = D[i] + N[i]; }

C 1: C 2: C 3: C 4: C 5: C 6: n for

C 1: C 2: C 3: C 4: C 5: C 6: n for (i = 1; i <= n; i++) { A[i] = B[i] + C[i]; D[i] = E[i] + F[i]; E[i+1] = A[i] + G[i]; H[i] = F[i] + B[i]; B[i+1] = E[i+1] + M[i]; F[i+1] = D[i] + N[i]; } CFC’s (só os vértices) e digrafo reduzido: 1º: C 1 -C 3 -C 5 2º: C 2 -C 6 3º: C 4

n O laço original pode ser decomposto em tantos laços quantos forem os CFC’s:

n O laço original pode ser decomposto em tantos laços quantos forem os CFC’s: C 1: C 3: C 5: for (i = 1; i <= n; i++) { A[i] = B[i] + C[i]; E[i+1] = A[i] + G[i]; B[i+1] = E[i+1] + M[i]; } C 2: C 6: for (i = 1; i <= n; i++) { D[i] = E[i] + F[i]; F[i+1] = D[i] + N[i]; } C 4: for (i = 1; i <= n; i++) { H[i] = F[i] + B[i]; } Em muitos casos é mais fácil aplicar técnicas de paralelização em laços menores do que em laços maiores

7. 6. 3 – Algoritmo para determinar os CFC’s de um digrafo n Será

7. 6. 3 – Algoritmo para determinar os CFC’s de um digrafo n Será apresentado o Algoritmo de Kosaraju (Sambasiva Rao Kosaraju - Indiano) n Faz duas buscas em profundidade no digrafo n Dividido em quatro passos n O algoritmo não será provado, mas apenas ilustrado

Passo 1: Fazer uma busca em profundidade em G, numerando os vértices no final

Passo 1: Fazer uma busca em profundidade em G, numerando os vértices no final da chamada recursiva de cada um n Exemplo: G G

Passo 2: Construir um novo digrafo Gr, obtido a partir de G, invertendo-se a

Passo 2: Construir um novo digrafo Gr, obtido a partir de G, invertendo-se a direção de todos os seus arcos (grafo reverso) G n Exemplo: Gr Manter a numeração obtida no passo 1

Passo 3: Fazer uma busca em profundidade em Gr, começando do vértice numerado no

Passo 3: Fazer uma busca em profundidade em Gr, começando do vértice numerado no passo 1 com o número mais alto n Exemplo: Gr Se a busca não visitar todos os vértices de Gr, começar novamente do vértice não visitado de numeração mais alta

Passo 4: Cada árvore na floresta da busca em profundidade em Gr, resultante do

Passo 4: Cada árvore na floresta da busca em profundidade em Gr, resultante do Passo 3, contém os vértices de um CFC de G n Exemplo: Os vértices dos CFC’s são: 1º: 2º: 3º: 4º: 5, 6, 7 1, 3, 2, 4 9, 8 10