Logo Passei Direto
Buscar
Material
páginas com resultados encontrados.
páginas com resultados encontrados.
left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

Prévia do material em texto

TEORIA DOS GRAFOS 
AULA 2 
 
 
 
 
 
 
 
 
 
Prof. Guilherme Augusto Pianezzer 
 
 
 
 
 
 
2 
CONVERSA INICIAL 
Caro(a) leitor(a), em conteúdos anteriores, determinamos o que é um 
grafo, avaliando as suas principais características. Além disso, você começou 
seus primeiros ensaios programando um grafo em Python. Agora, vamos 
investigar os conceitos de conexidade e conectividade, além de elaborar, 
também em Python, um algoritmo conhecido como algoritmo de Malgrange, 
responsável por determinar as componentes conexas de um grafo orientado. 
TEMA 1 – CONEXIDADE E CONECTIVIDADE 
Na etapa anterior, avaliamos que é possível determinar percursos saindo 
de um vértice e chegando a um outro. O que temos é uma lista que indica os 
vértices subsequentes por que precisamos passar até chegar ao vértice final. 
Entretanto, nem sempre é possível estabelecer um percurso entre dois pontos. 
Esse conceito é essencial para os principais algoritmos de determinação de 
caminho mínimo. 
1.1 Conexidade em grafos não orientados 
Vamos considerar os grafos não orientados, como o grafo 𝐺1 da figura a 
seguir. 
Figura 1 – Grafo não-orientado composto de 5 vértices 
 
Nesse exemplo, é evidente que existe um percurso saindo de um vértice 
qualquer e chegando em qualquer outro. Isso fica ainda mais claro quando 
observamos o grafo 𝐺2 da figura a seguir. 
 
 
 
3 
Figura 2 – Grafo não-orientado composto de 7 vértices 
 
Nesse caso, observamos que não há, por exemplo, percurso saindo do 
primeiro vértice e chegando ao sétimo. Utilizando os conceitos da etapa anterior, 
note que o fecho transitivo de 1 não contém 7. Afinal: 
𝑅(1) = {2,3,4} 
𝑅(7) = {5,6} 
A situação indica que o fecho transitivo de 1 contém os vértices atingíveis 
a partir de 1, enquanto o fecho transitivo de 7 contém os vértices atingíveis a 
partir do vértice 7. O que difere o grafo 𝐺1 do grafo 𝐺2 é que o primeiro é conexo 
– portanto, apresenta a propriedade da conexidade. 
Quando o grafo é conexo, como o grafo 𝐺1, podemos ir de um vértice a 
outro transitando pelas arestas. Aqui, percebemos uma característica do grafo 
𝐺1. Para chegar ao vértice 4, é essencial, independentemente do vértice de 
partida, passar pela aresta que liga o vértice 5 ao vértice 4. A esse vértice damos 
o nome de ponte do grafo. A sua remoção faz do grafo 𝐺1, um grafo não conexo. 
1.2 Conexidade em grafos orientados 
Vamos considerar agora os grafos orientados. Existem quatro tipos de 
conexidade analisadas nesse tipo de grafo. A importância de estabelecer 
classificações é que cada algoritmo vai funcionar para determinados casos. 
1.2.1 Grafo não conexo 
Um grafo não conexo é aquele em que pelo menos um par de vértices 
não é ligado por nenhuma cadeia. É o caso do grafo a seguir. 
 
 
 
4 
Figura 3 – Grafo orientado não conexo 
 
1.2.2 Grafo simplesmente conexo (s-conexo) 
Um grafo orientado é chamado simplesmente conexo ou s-conexo 
quando a sua versão não orientada for conexa. 
Figura 4 – Dois grafos orientados simplesmente conexos 
 
Veja o caso da Figura 4, que apresenta dois grafos distintos orientados. 
Perceba que, a partir do vértice 4, o vértice 1 não é atingível. Entretanto, se esse 
grafo fosse não orientado, todos os vértices seriam atingíveis por um outro 
vértice qualquer. Afinal, o que provoca a dificuldade de atingir os vértices é 
justamente a orientação. Assim, definimos o grafo simplesmente conexo como 
aquele que é conexo em sua versão não orientada. 
1.2.3 Grafo semi-fortemente conexo (sf-conexo) 
Um grafo orientado é semi-fortemente conexo ou s-conexo se, dado 
qualquer par de vértices 𝑣1, 𝑣2, pelo menos um deles é atingível a partir do outro, 
isto é, existe um caminho que sai de 𝑣1 e chega em 𝑣2 ou existe um caminho 
que sai de 𝑣2 e chega em 𝑣1. 
 
 
 
5 
Figura 5 – Dois grafos orientados semi-fortemente conexos 
 
Na Figura 5, observamos dois grafos semi-fortemente conexos. Para 
realizar esse teste, você precisa selecionar todos os pares possíveis de vértices. 
Veja, por exemplo, o que ocorre quando comparamos o vértice 1 e o vértice 4. 
O vértice 4 é atingível pelo vértice 1, embora o vértice 1 não seja atingível pelo 
vértice 4. Isso é válido para quaisquer pares de vértices, sendo suficiente para 
definir o caso semi-fortemente conexo. 
1.2.4 Grafo fortemente conexo (f-conexo) 
Um grafo fortemente conexo, ou ainda f-conexo, é um grafo em que, 
considerando quaisquer 𝑣1, 𝑣2, podemos atingir 𝑣1 saindo de 𝑣2 e 𝑣2 saindo de 
𝑣1. 
Figura 6 – Dois grafos orientados fortemente conexos 
 
Aqui, podemos pontuar que o teste de conexidade envolve a análise de 
vários pares de vértices, que crescem da forma 𝑛2 para a quantidade 𝑛 de 
vértices. Isso justifica a complexidade dos algoritmos computacionais que 
veremos em outras etapas. 
TEMA 2 – GRAFO REDUZIDO 
Nos algoritmos que vamos resolver, é interessante identificar a existência 
de componentes f-conexas, isto é, subgrafos construídos em busca dessa 
propriedade. 
 
 
6 
2.1 Componente f-conexa 
Para esses algoritmos, vamos selecionar um dado grafo, com subgrafos 
que também sejam f-conexa. Se identificarmos algum que seja maximal, isto é, 
que não faça parte de outros que também são f-conexos, esse será uma 
componente f-conexa. Veja, por exemplo, o grafo da Figura 7. 
Figura 7 – Grafo orientado composto de 16 vértices 
 
Percebemos que esse grafo é sf-conexo, isto é, semi-fortemente conexo. 
Entretanto, podemos selecionar subgrafos que são f-conexos maximais. Nesse 
caso, temos quatro possibilidades, que são as componentes f-conexas do grafo 
da Figura 7. Tais componentes estão apresentadas na figura a seguir. 
Figura 8 – Quatro componentes f-conexas do grafo da Figura 7 
 
2.2 Grafo reduzido 
Por definição, um grafo reduzido 𝐺𝑟 = (𝑉𝑟 , 𝐸𝑟) é um grafo formado a partir 
de 𝐺 que atende alguns requisitos. Para formá-lo, cada componente f-conexa 
será um vértice de 𝐺𝑟. Para formar as conexões entre os vértices 𝑣1, 𝑣2 ∈ 𝐺𝑟 
devemos verificar se existe algum vértice de 𝑣1 (lembrando que 𝑣1 é uma 
componente f-conexa, isto é, é um grafo) atingível a algum vértice de 𝑣2. Nesse 
 
 
7 
caso, formamos um arco saindo de 𝑣1 para 𝑣2. Embora a definição textual pareça 
complicada, observe a Figura 9, que apresenta o grafo reduzido da Figura 8. 
Figura 9 – Grafo reduzido da Figura 8 
 
Note que cada componente f-conexa, no grafo reduzido, se tornou um 
vértice desse novo grafo. Note também que a classificação da conexidade do 
grafo reduzido é a mesma do grafo original. Nesse caso, ambos são sf-conexos. 
TEMA 3 – ALGORITMO: DETERMINAÇÃO DAS COMPONENTES F-CONEXAS 
Você deve ter percebido que as componentes f-conexas permitem reduzir 
grafos e escrever as suas versões mais simples. Isso é especialmente 
interessante quando resolvermos problemas de logística e otimização de 
caminhos. 
3.1 Estruturas de dados para o algoritmo de Malgrange 
Nesta seção, vamos determinar o algoritmo de Malgrange, que por sua 
vez permite determinar as componentes f-conexas de determinado grafo. Para 
isso, precisamos resgatar algumas estruturas de dados que devem ser definidas, 
computacionalmente, em Python. 
Nesse caso, escolhemos 𝐺 = (𝑉, 𝐸). O grafo analisado será composto 
pelos vértices 𝑉 e pelas arestas 𝐸. Note que 𝑉 é uma lista de vértices, enquanto 
𝐸 será definido pela lista de adjacência. Como exemplo, vamos utilizar o grafo 
da Figura 10. 
Figura 10 – Grafo para análise do algoritmo de Malgrange 
 
 
 
8 
Aqui, devemos lançar as informações sobre os vértices 
computacionalmente, considerando descendentes e ascendentes. Embora 
tenhamos lançado, em conteúdo anterior, apenas os vértices adjacentes, é 
interessante classificá-los (descendentes ou ascendentes), pois se trata de um 
grafo orientado. 
Tabela 1 – Lista de adjacência para o grafo orientado da Figura 10 
Vértice Vértices 
descendentesVértices 
ascendentes 
1 2,3 5 
2 4,6 1,5 
3 4 1,4 
4 3 2,3 
5 1,2 6,7 
6 5 2,8 
7 5,8 8 
8 6,7 7 
Além disso, o algoritmo vai utilizar algumas variáveis. Vamos chamar de 
𝑌 o vetor que armazena os vértices que ainda não pertencem a uma 
componente. 𝐼 será um indexador que monitora as componentes conexas 
encontradas. Também vamos definir o vetor 𝑅+ e o vetor 𝑅−, que armazenam, 
respectivamente, os descendentes e os ascendentes do vértice analisado. 
3.2 Algoritmo de Malgrange 
Vamos abordar o algoritmo de Malgrange em duas etapas. Na primeira, 
vamos apresentar a estrutura do algoritmo, considerando os laços de repetição 
que precisam ser utilizados. Também vamos reutilizar o exemplo citado para 
aplicar o algoritmo e verificar o resultado encontrado. 
Algoritmo de Malgrange: determinando as componentes f-conexas de 
um grafo orientado. 
 Início Grafo 𝐺 = (𝑉, 𝐸) 
 𝑌 ← 𝑉 
𝐼 ← 0 
Enquanto 𝑌 ≠ ∅ fazer 
 𝐼 ← 𝐼 + 1; 
 
 
9 
𝑅+ ← ∅; 
𝑅− ← ∅; 
Escolher 𝑣 ∈ 𝑌; 
𝑊 ← ∅; 𝑅+ ← {𝑣}; 
 Enquanto 𝑁+(𝑅+) − 𝑅+ ≠ ∅ fazer 
 𝑊 ← 𝑁+(𝑅+(𝑣)) − 𝑅+(𝑣); 
𝑅+ ← 𝑅+(𝑣) ∪ 𝑊; 
𝑊 ← ∅; 𝑅− ← {𝑣}; 
 Enquanto 𝑁−(𝑅−) − 𝑅− ≠ ∅ fazer 
𝑊 ← 𝑁−(𝑅−(𝑣)) − 𝑅−(𝑣); 
𝑅− ← 𝑅−(𝑣) ∪ 𝑊; 
Componente(𝑰) ← 𝑹+ ∪ 𝑹−; 
𝑌 ← 𝑌-componente(𝐼); 
Fim. 
TEMA 4 – APLICAÇÃO DO ALGORITMO DE MALGRANGE 
A partir do grafo da Figura 10, podemos aplicar o algoritmo de Malgrange 
para determinar as componentes f-conexas e escrever o seu grafo em forma 
reduzida computacionalmente. 
4.1 Estudo de caso 
Para realizar esse papel, devemos acompanhar a execução do algoritmo, 
investigando o que ocorre com os dados. A título de aprendizado, é interessante 
que você escolha outros exemplos e faça a execução “a lápis” do algoritmo, para 
verificar se é capaz de determinar as f-componentes e compreender, de fato, o 
que o algoritmo está fazendo. 
Imagine que você inicializa o algoritmo considerando os vértices e a lista 
de adjacência da Tabela 1. Ao inicializar o algoritmo, as primeiras linhas fazem 
com que: 
𝑌 ← 𝑉 
𝐼 ← 0 
𝑅+ ← ∅ 
𝑅− ← ∅ 
𝐼 ← 1 
 
 
10 
Na sequência, escolhemos um vértice qualquer. Por exemplo, o vértice 1. 
Nesse caso, teremos: 
𝑣 ← {1} 
𝑊 = ∅ 
𝑅+ ← {1} 
Como 𝑁+(𝑅+) = {2,3} pela lista de adjacência, devemos proceder o loop, 
dado que 𝑁+(𝑅+) − 𝑅+ ≠ ∅. Logo, atualizamos 𝑊 dado por 𝑊 ← 𝑁+(𝑅+(𝑣)) −
𝑅+(𝑣), obtendo 𝑊 = {2,3}. Atualizamos 𝑅+ ← 𝑅+(𝑣) ∪ 𝑊, obtendo 𝑅+ = {1,2,3}. 
Operamos novamente, encontrando 𝑁+(𝑅+) = {2,3,4,6}. Como 𝑁+(𝑅+) −
𝑅+ ≠ ∅, atualizamos novamente 𝑊, dado por 𝑊 ← 𝑁+(𝑅+(𝑣)) − 𝑅+(𝑣), obtendo 
𝑊 = {4,6}, e atualizamos novamente 𝑅+, obtendo 𝑅+ ← 𝑅+(𝑣) ∪ 𝑊, dado por 
𝑅+ = {1,2,3,4,6}. 
Realizamos o teste mais uma vez. Temos que 𝑁+(𝑅+) = {2,3,4,5,6}. 
Como, mais uma vez, 𝑁+(𝑅+) − 𝑅+ ≠ ∅, atualizamos 𝑊 dado por 𝑊 ←
𝑁+(𝑅+(𝑣)) − 𝑅+(𝑣), obtendo 𝑊 = {5}, e atualizamos novamente 𝑅+, obtendo 
𝑅+ ← 𝑅+(𝑣) ∪ 𝑊, dado por 𝑅+ = {1,2,3,4,5,6}. 
Ao realizar o teste uma última vez, observamos que 𝑁+(𝑅+) − 𝑅+ = ∅. 
Assim, podemos partir para o próximo loop. Esse conjunto é o fecho transitivo 
direto do primeiro vértice. 
Para continuar, atualizamos 𝑅− e 𝑊, obtendo: 
𝑊 = ∅ 
𝑅− = {1} 
Fazemos algo similar a partir daqui, utilizando como verificador os vértices 
ascendentes. Temos que 𝑁−(𝑅−) = {5}, de forma que 𝑁−(𝑅−) − 𝑅− ≠ ∅. Logo, 
atualizamos 𝑊, dado por 𝑊 ← 𝑁−(𝑅−(𝑣)) − 𝑅−(𝑣), obtendo 𝑊 = {5}. Também 
atualizamos 𝑅− ← 𝑅−(𝑣) ∪ 𝑊, obtendo 𝑅− = {5}. 
Operamos novamente, encontrando 𝑁−(𝑅−) − 𝑅− ≠ ∅. Como ainda é 
válida a condição do loop, dado que 𝑁−(𝑅−) = {6,7}, atualizamos 𝑊 dado por 
𝑊 ← 𝑁−(𝑅−(𝑣)) − 𝑅−(𝑣), obtendo 𝑊 = {6,7}, e atualizamos 𝑅− ← 𝑅−(𝑣) ∪ 𝑊, 
obtendo 𝑅− = {5,6,7}. 
Realizando o teste mais uma vez, verificamos que 𝑁−(𝑅−) = {2,6,7,8}. 
Assim, 𝑁−(𝑅−) − 𝑅− ≠ ∅. Logo, atualizamos 𝑊 dado por 𝑊 ← 𝑁−(𝑅−(𝑣)) −
 
 
11 
𝑅−(𝑣), obtendo 𝑊 = {2,8}; também atualizamos 𝑅− ← 𝑅−(𝑣) ∪ 𝑊, obtendo 𝑅− =
{2,5,6,7,8}. 
Ao aplicar o teste outra vez, verificamos que 𝑁−(𝑅−) = {1,2,5,6,7,8}. 
Assim, 𝑁−(𝑅−) − 𝑅− ≠ ∅. Logo, atualizamos 𝑊 dado por 𝑊 ← 𝑁−(𝑅−(𝑣)) −
𝑅−(𝑣), obtendo 𝑊 = {1}, e atualizamos 𝑅− ← 𝑅−(𝑣) ∪ 𝑊, obtendo 𝑅− =
{1,2,5,6,7,8}. 
Agora, aplicamos o teste mais uma vez, saindo do loop, dado que 
𝑁−(𝑅−) = {1,2,5,6,7,8}. Ao sair do loop, obtemos: 
𝑅+{𝑣} = {1,2,3,4,5,6} 
𝑅−{𝑣} = {1,2,5,6,7,8} 
Note que: 
𝑅+(𝑣) ∩ 𝑅−(𝑣) = {1,2,5,6} 
Atualizando: 
Componente(1) = {1,2,5,6} 
Essa intersecção já é uma componente f-conexa do grafo da Figura 10 
(observe a figura). Aqui, atualizamos o vetor 𝑌, que carrega os vértices que ainda 
não estão em alguma componente f-conexa. Nesse caso, tínhamos que 𝑌 =
{1,2,3,4,5,6,7,8}. Atualizando por 𝑌 ← 𝑌 − 𝐶𝑜𝑚𝑝𝑜𝑛𝑒𝑛𝑡𝑒(1), passamos a ter 𝑌 =
{3,4,7,8}. Veja que, como 𝑌 ≠ ∅, devemos continuar aplicando o algoritmo. 
TEMA 5 – GRAFO REDUZIDO DO ESTUDO DE CASO 
A título de exercício, é interessante que você continue a aplicação do 
algoritmo em outras duas iterações, para verificar se ele é capaz de encontrar 
as outras duas componentes f-conexas do problema. O que você irá encontrar 
será componente(2) = {3,4} e componente(3) = {7,8}. 
5.1 Grafo reduzido 
O grafo da Figura 10 apresenta, portanto, 3 componentes f-conexas que 
podem ser determinadas computacionalmente. Verificamos que elas são dadas 
por: 
componente(1) = {1,2,5,6} 
componente(2) = {3,4} 
componente(3) = {7,8} 
 
 
12 
Note, portanto, que a versão reduzida desse grafo pode ser representada 
pela Figura 11. 
Figura 11 – Grafo da Figura 10 reformulado em forma reduzida 
 
Revendo a classificação dos grafos a respeito da conexidade, 
percebemos que ambos são grafos sf-conexos. Note que a versão reduzida do 
grafo mantém a sua classificação. 
NA PRÁTICA 
Figura 12 – Grafo direcionado com 14 vértices 
 
Considere o grafo da Figura 12, que apresenta uma rede de fofocas entre 
14 pessoas diferentes. O sentido da seta mostra para quem cada pessoa conta 
a fofoca. 
a) Utilizando a linguagem de programação Python e os conhecimentos 
desenvolvidos na última atividade prática, forneça, computacionalmente, 
a lista de adjacência (diferenciando descendentes de ascendentes) do 
problema. 
b) Implemente, em Python, o algoritmo de Malgrange, encontrando as 
componentes f-conexas do grafo fornecido. 
 
 
13 
c) Represente, graficamente (pesquise o uso de bibliotecas em Python), o 
grafo da Figura 12 e a sua versão reduzida pelas componentes f-conexas 
da alternativa b. 
FINALIZANDO 
Caro(a) leitor(a), com os estudos desta etapa, você já está desenvolvendo 
a sua maturidade computacional. Esperamos que você consiga programar os 
seus primeiros algoritmos em Python, para resolver problemas da teoria dos 
grafos. Com isso, já estabelecemos as bases para começar a discutir problemas 
de caminho mínimo, de grande interesse nas áreas de logística.

Mais conteúdos dessa disciplina