Figura 1
Uma árvore é um grafo conexo e acíclico. Um grafo conexo não tem vértice ou partes isoladas do restante do grafo. Tudo precisa estar conectado. Um grafo acíclico é, como o nome diz, um grafo sem ciclos, ou seja, sem um circuito fechado formado por suas arestas e vértices. Uma árvore possui um vértice inicial chamado raiz, os vértices seguintes são seus filhos que também podem ter filhos, sendo vértices pai desta forma, e assim por diante até chegar às folhas, ou seja, até chegar àqueles vértices que não possuem filhos.
Como este exemplo abaixo:
Figura 2
Sabendo o que é uma árvore podemos agora descobrir o que é uma árvore geradora. Árvore Geradora é aquela originária de um grafo G e que seja formada por todos os vértices de G.
Figura 3
Dá pra notar que existirão várias árvores geradoras de um mesmo grafo:
Figura 4
Uma delas é especial, trata-se da Árvore Geradora Mínima. Uma Árvore Geradora Mínima (MST) é uma árvore geradora de um grafo G ponderado que tem custo total mínimo.
Figura 5 - Árvore geradora mínima desenhada em vermelho
Há alguns algoritmos utilizados para a extração de MST's e o utilizado neste exemplo foi o Algoritmo de Prim:
Ok, vamos explicar como o algoritmo funciona.
O Algoritmo de Prim consiste em escolher um vértice inicial conhecido como raiz, sem aresta alguma ainda, do qual a árvore irá crescer. Então fazemos a árvore crescer, ou seja, vamos visitando outros vértices adicionando a aresta que está entre o vértice de partida e o novo à árvore.
Esta escolha é feita levando-se em conta o peso das arestas analisadas. A cada descoberta de nova aresta durante o crescimento da árvore o seu peso é colocado em uma lista de prioridades e substitui o seu valor antigo na lista caso seja menor. Esta lista será usada para saber qual aresta deve ser inserida na árvore. A aresta de menor peso na lista de prioridades é sempre escolhida e inserida na árvore.
Caso mais adiante outra aresta que chegue o mesmo vértice tenha uma peso menor que o atual a antiga aresta é retirada e a nova descoberta é inserida. Quando não houver mais vértices para serem visitados teremos uma árvore geradora com a menor soma de pesos de arestas possível, ou seja, encontraremos a Árvore Geradora Mínima.
Agora vamos falar de Comunidades.
Comunidades são, basicamente, conjuntos de vértices isolados. É possível obter comunidades ao retirar arestas da árvore geradora mínima.
Figura 6 - Árvore geradora mínima
Da árvore geradora mínima acima podemos retirar a aresta entre os vértices 'b' e 'e' para obter duas comunidades, chamadas aqui de A e B:
Figura 7
Há entretanto alguns métodos diferentes para se obter comunidades à partir da remoção de arestas da MST. Utilizaremos dois deles aqui.
A primeira forma é decidir um número k de comunidades que se deseja obter e então retirar as k-1 maiores arestas da MST.
Por exemplo, para se obter duas comunidades (k = 2), ou agrupamentos, da MST utilizada acima bastaria retirar as k-1 maiores arestas, ou seja, se k = 2 então k - 1 = 1, bastaria remover a maior aresta da MST. Neste exemplo a aresta a ser retirada será a entre 'b' e 'c', que tem peso 4, formando os agrupamentos A e B:
Figura 8
Se quisermos um número maior, como k = 5 agrupamentos, basta aplicar a mesma regra e retirar as k-1 maiores arestas, ou seja, as 4 maiores, formando os agrupamentos A, B, C, D e E:
Figura 9
Note como há vários vértices isolados, comunidades de um vértice só, isso se deve em parte ao tamanho do grafo utilizado mas o maior problema foi o método utilizado. Note no primeiro exemplo, com k = 2, como seria possível formar agrupamentos com um número de vértices mais parecidos mas ao invés disso o método utilizado fez com que se formasse uma comunidade com um vértice e outra com cinco. Muitas vezes não é desejável que haja tamanha discrepância entre o tamanho das comunidades e por isso utilizaremos agora outro método.
Desta vez ao invés de simplesmente remover as k-1 maiores arestas da MST para se obter k comunidades iremos utilizar o seguinte cálculo para descobrir qual é a melhor aresta a ser removida da MST de forma a deixar o número de vértices em cada agrupamento mais parecido entre si:
w(A,B) * min{Na, Nb}
Onde w(A,B) é o peso da aresta analisada e min{Na, Nb} é o mínimo entre o número de vértices do grupo A (Na) e o número de vértices do grupo B (Nb), pois sempre que uma aresta for removida serão formados exatamente dois agrupamentos (A e B, neste caso). Após fazer os cálculos para todas as arestas selecionaremos o maior valor e removeremos a aresta correspondente.
Na prática ficaria assim, utilizando o seguinte grafo e sua MST para criar dois agrupamentos:
Figura 10 - Grafo G com sua MST escurecida
Figura 11 - MST resultante sem as outras arestas do grafo G
Fazemos então o cálculo descrito para cada aresta da MST:
bc: 8 * min{2,7} = 8*2 = 16
cd: 7 * min{2,7} = 7*2 = 14
de: 9 * min{1,8} = 9*1 = 9
ci: 2 * min{1,8} = 2*1 = 2
cf: 4 * min{3,6} = 4*3 = 12
fg: 2 * min{2,7} = 2*2 = 4
gh: 1 * min{1,8} = 1*1 = 1
O maior valor obtido corresponde a aresta 'bc', que será então removida, obtendo assim dois agrupamentos:
Figura 12
Faremos agora o processo para a obtenção de cinco comunidades. Para isso basta repetir os cálculos com valores e arestas atualizados a cada remoção:
cd: 7 * min{2,5} = 7*2 = 14
de: 9 * min{1,6} = 9*1 = 9
ci: 2 * min{1,6} = 2*1 = 2
cf: 4 * min{3,4} = 4*3 = 12
fg: 2 * min{2,5} = 2*2 = 4
gh: 1 * min{1,6} = 1*1 = 1
Desta vez o maior valor obtido corresponde a aresta 'cd', então removendo-a teremos:
Figura 13
Temos três agrupamentos. Retiremos mais uma aresta para obter quatro agrupamentos:
de: 9 * min{1,1} = 9*1 = 9
ci: 2 * min{1,4} = 2*1 = 2
cf: 4 * min{2,3} = 4*2 = 8
fg: 2 * min{2,3} = 2*2 = 4
gh: 1 * min{1,4} = 1*1 = 1
A aresta 'de' deve ser removida agora:
Figura 14
Calculamos uma última vez:
ci: 2 * min{1,4} = 2*1 = 2
cf: 4 * min{2,3} = 4*2 = 8
fg: 2 * min{2,3} = 2*2 = 4
gh: 1 * min{1,4} = 1*1 = 1
Removemos 'cf' e obtemos os cinco agrupamentos abaixo, como desejado inicialmente:
Figura 15
Podemos notar que desde a primeira remoção este método acabou por evitar que vértices ficassem isolados. É claro que depois de algumas iterações alguns vértices isolados acabaram formando comunidades mas houve uma divisão mais parecida com relação à quantidade de vértices em cada comunidade por fim.
O código em Python utilizado para o primeiro método foi o seguinte:
O código usado para o segundo método foi este:
Caso tenha alguma dúvida, por favor, deixe seu comentário aqui no blog.
Referências das imagens:
http://luizricardo.org/2014/03/desafio-de-programacao-algoritmo-lca-lower-common-ancestor/
Figuras 3 e 4
http://theory.cs.uvic.ca/inf/subg/Spanning.html
Figura 5
http://redesavanzadas2013b.blogspot.com.br/
Figura 10
https://www.cs.indiana.edu/~achauhan/Teaching/B403/LectureNotes/09-mst.html


















Nenhum comentário:
Postar um comentário