• Nenhum resultado encontrado

4.2 An´ alise de Algoritmos de Redes Fundamentais

4.2.2 Rede de Barab´ asi e Albert

O modelo de Barab´asi e Albert ´e um dos mais bem sucedidos para a gera¸c˜ao de redes livres de escala. Por isso, podemos utiliz´a-lo para entender como essas redes podem ser geradas. Suas regras b´asicas s˜ao: o crescimento da rede e a liga¸c˜ao preferencial. Ao longo desta se¸c˜ao, perceberemos como estas regras se traduzem para o algoritmo, ressaltando as principais diferen¸cas entre este modelo e o de rede aleat´oria. Mostraremos tamb´em algoritmos deste modelo de acordo com o c´odigo padr˜ao e compararemos com o c´odigo r´apido, discutindo suas principais vantagens.

4.2.2.1 C´odigo Padr˜ao

Partindo de um c´odigo que gere uma rede aleat´oria, necessitamos realizar uma grande mudan¸ca estrutural para obtermos o c´odigo padr˜ao deste modelo, pois para este tipo de rede a etapa de constru¸c˜ao passa a ser dividida explicitamente entre: (i) defini¸c˜ao da condi¸c˜ao inicial e

(ii) itera¸c˜ao das regras de conex˜ao. Desta forma, de acordo com o passo (1) do algoritmo deste modelo (se¸c˜ao 3.1), a condi¸c˜ao inicial desta rede ´e dada por N0 = M0+ 1 v´ertices conectados

entre si. Deve-se ressaltar, entretanto, que agora a vari´avel M n˜ao significa a quantidade total de arestas que estar˜ao presentes na rede depois de encerrada sua constru¸c˜ao. Essa vari´avel agora informa a quantidade de vizinhos M0 que cada v´ertice adicionado `a rede ter´a. O la¸co

abaixo, mostra como esta condi¸c˜ao pode ser descrita.

ite_m = 0 ite_m2 = 0 while ite_m < M: ite_m2 = ite_m + 1 while ite_m2 < M+1: lista_adj[ite_m].append(ite_m2) lista_adj[ite_m2].append(ite_m) lista_k[ite_m] = lista_k[ite_m] + 1 lista_k[ite_m2] = lista_k[ite_m2] + 1 ite_m2 = ite_m2 + 1 ite_m = ite_m + 1

Os passos (2) e (3) deste algoritmo representam a parte na qual s˜ao ressaltadas as regras b´asicas deste modelo. Perceba que a probabilidade de qualquer v´ertice de uma rede aleat´oria se conectar a um outro v´ertice qualquer da rede ´e constante, sendo igual a 1/N para o c´odigo exemplificado antes, mas depois que ele est´a conectado, a probabilidade passa a ser nula. Para o modelo de Barab´asi e Albert, essa probabilidade ´e dinˆamica, ou seja, ela se altera toda vez que um novo v´ertice ´e adicionado `a rede. Isso ocorre porque a rede agora deixa de ser est´atica, isto ´e, a quantidade de v´ertices que a comp˜oe est´a em constante altera¸c˜ao e aumenta com o tempo. Devido a isso, inicialmente devemos criar duas novas fun¸c˜oes: uma para calcular as probabilidades de conex˜ao baseadas no grau de conectividade de cada v´ertice que j´a est´a na rede (calcula prob) e outra para encontrar com qual deles o v´ertice que est´a entrando na rede ir´a se conectar (encontra v2). Estas fun¸c˜oes nos auxiliar˜ao durante o processo de crescimento da rede. A seguir apresentaremos o la¸co principal deste c´odigo, o qual itera sobre todos os v´ertices a serem acrescentados `a rede e suas conex˜oes:

ite_n = M+1 while ite_n < N: v1 = ite_n calcula_prob(ite_n) ite_m = 0 while ite_m < M: prob_v2 = random() v2 = encontra_v2(ite_n)

while v2 in lista_adj[v1]: prob_v2 = random() v2 = encontra_v2(ite_n) lista_adj[v1].append(v2) lista_adj[v2].append(v1) lista_k[v1] = lista_k[v1] + 1 lista_k[v2] = lista_k[v2] + 1 ite_m = ite_m + 1 lista_todas_prob = [0]*N lista_soma_prob = [0]*N ite_n = ite_n + 1

H´a duas novas vari´aveis (listas), lista todas prob e lista soma prob, `as quais s˜ao atribu´ıdas listas compostas apenas por zeros nessa parte do c´odigo, mas cuja utilidade ser´a explicada adiante.

Ao longo deste processo, necessitamos chamar as duas fun¸c˜oes relacionadas com o c´alculo de probabilidades. Mas como estas s˜ao definidas? A primeira fun¸c˜ao, aquela que calcula a probabilidade de conex˜ao associada a cada v´ertice que j´a est´a na rede, ´e definida da seguinte maneira:

def calcula_prob(ite_n): soma_prob = 0

soma_lista_k = float(sum(lista_k)) ite_p = 0

while ite_p < ite_n:

prob = float(lista_k[ite_p])/soma_lista_k lista_todas_prob[ite_p] = prob

soma_prob = soma_prob + prob

lista_soma_prob[ite_p] = soma_prob ite_p = ite_p + 1

Essa fun¸c˜ao recebe como parˆametro um n´umero que diz quantos v´ertices j´a fazem parte da rede. Observe que o c´alculo das probabilidades dos v´ertices segue fielmente sua defini¸c˜ao dada pela equa¸c˜ao (3.1), ou seja, a probabilidade de um v´ertice que est´a na rede receber a liga¸c˜ao do v´ertice que est´a entrando ´e matematicamente escrita como o grau de conectividade do v´ertice em quest˜ao divido pela soma do grau de conectividade de todos os v´ertices da rede. Veja que a fun¸c˜ao float ´e usada para fazer com que o computador enxergue um n´umero inteiro como um n´umero real, de modo que a divis˜ao que calcula prob n˜ao seja efetuada como uma divis˜ao inteira. Dependendo da linguagem de programa¸c˜ao utilizada, essa precau¸c˜ao pode ser necess´aria. As listas lista todas prob e lista soma prob s˜ao utilizadas para, respectivamente, armazenar as probabilidades de cada v´ertice se conectar com o novo v´ertice e armazenar a soma cumulativa dessas probabilidades.

A segunda fun¸c˜ao, aquela que encontra com quem o v´ertice que est´a entrando na rede ir´a se conectar, ´e definida da seguinte maneira:

def encontra_v2(ite_n, prob_v2): ite_n2 = 0

while ite_n2 < ite_n:

if lista_soma_prob[ite_n2] >= prob_v2: return ite_n2

ite_n2 = ite_n2 + 1

Note que a escolha de conex˜ao entre os v´ertices ´e representada pela condi¸c˜ao de que a soma cumulativa das probabilidades associadas a um dado v´ertice seja maior ou igual ao valor aleat´orio armazenado em prob v2, o qual ´e novamente sorteado a cada nova tentativa de conex˜ao. Esta fun¸c˜ao ´e de extrema importˆancia para este algoritmo, pois ela caracteriza a regra de liga¸c˜ao preferencial.

4.2.2.2 C´odigo R´apido

O c´odigo padr˜ao ´e uma tradu¸c˜ao do algoritmo apresentado na se¸c˜ao (3.1). Entretanto, o seu m´etodo do c´alculo de probabilidades requer muito tempo para ser executado. Isso pode ser evitado ao se utilizar o c´odigo r´apido para este modelo, o qual ´e facilmente justificado ao considerarmos a importˆancia da busca de algoritmos diferentes e mais eficientes que gerem um mesmo tipo de rede. Neste c´odigo, em particular, procuramos uma maneira de substituir o c´alculo das probabilidades apresentado no c´odigo anterior, para que possamos tornar sua execu¸c˜ao mais r´apida. A pergunta a ser respondida ´e: como podemos fazer isso? A resposta encontrada baseia-se na utiliza¸c˜ao de um um vetor de apoio que armazena todos os pares de v´ertices que realizaram uma conex˜ao. A figura (5.4) exemplifica o funcionamento deste tipo de estrutura no processo de constru¸c˜ao de uma rede.

Figura 4.3: Exemplifica¸c˜ao do vetor de apoio utilizado na constru¸c˜ao do c´odigo r´apido para o modelo de Barab´asi e Albert.

Desta forma, a escolha de um v´ertice para conex˜ao no c´odigo r´apido ´e dada pelo sorteio aleat´orio de um elemento do vetor de apoio (lista conexoes). Como podemos notar na figura (4.3), esta probabilidade ´e proporcional `a sua conectividade. A seguir apresentaremos o la¸co principal deste c´odigo:

ite_n = M + 1 while ite_n < N: v1 = ite_n v2 = lista_conexoes[int((conta_conex)*random())] while v2 in lista_adj[v1]: v2 = lista_conexoes[int((conta_conex)*random())] lista_adj[v1].append(v2) lista_adj[v2].append(v1) lista_k[v1] = lista_k[v1] + 1 lista_k[v2] = lista_k[v2] + 1 lista_conexoes[conta_conex] = v1 conta_conex = conta_conex + 1 lista_conexoes[conta_conex] = v2 conta_conex = conta_conex + 1 ite_n = ite_n + 1

Note que o uso das lista adj e lista k continua sendo o mesmo. Tamb´em temos uma nova vari´avel (conta conex) que guarda a quantidade de elementos no vetor de apoio at´e o momento e que auxilia diretamente no sorteio aleat´orio do v´ertice que ir´a se conectar. Essas vari´aveis devem ser atualizadas a cada nova aresta adicionada.