Você está na página 1de 12

Análise de complexidade assintótica de algoritmos em grafos por uma

abordagem empı́rica

Juliana M. Souza
Universidade do Estado do Rio de Janeiro (UERJ)
Rua São Francisco Xavier, 524, sala 6019B, 20550-900, Rio de Janeiro – RJ, Brazil
julianamacario.eng@gmail.com

Fabiano S. Oliveira
Universidade do Estado do Rio de Janeiro (UERJ)
Rua São Francisco Xavier, 524, sala 6019B, 20550-900, Rio de Janeiro – RJ, Brazil
fabiano.oliveira@ime.uerj.br

Valmir C. Barbosa
COPPE / Universidade Federal do Rio de Janeiro (UFRJ)
Caixa Postal 68511, 21941-972, Rio de Janeiro – RJ, Brazil
valmir@cos.ufrj.br

RESUMO
Muitos problemas de otimização do mundo real são melhor formulados através de
problemas em grafos, senão diretamente ao menos frequentemente em combinação com outras
técnicas. Enquanto nos casos mais simples a complexidade computacional assintótica resultante
pode ser determinada analiticamente, muitas vezes o método de solução envolve o uso de técnicas
de otimização ou meta-heurı́sticas de garantias de desempenho desconhecidas. Em casos como
esses, pode ser útil contar com métodos empı́ricos para avaliar a complexidade, mas até hoje um
estudo abrangente desta possibilidade não foi realizado. Neste artigo, fornecemos alguns avanços
para resolver o problema. Para alguns algoritmos clássicos, demonstramos como o uso de ferra-
mentas prontamente disponı́veis pode levar à correta avaliação da complexidade, sugerindo que a
abordagem empı́rica pode ser viável na prática.

PALAVRAS CHAVE. Análise empı́rica. Algoritmos. Complexidade assintótica.

TAG – Teoria e Algoritmos em Grafos

ABSTRACT
Many real-world optimization problems are best formulated as problems on graphs, so-
metimes directly but frequently in combination with other techniques. While in the simplest cases
the resulting asymptotic computational complexity can be determined analytically, often the solu-
tion method involves the use of optimization techniques or meta-heuristics of unknown performance
guarantees. In cases such as these it might be useful to rely on empirical methods to assess comple-
xity, but to date a comprehensive study of this possibility has not been undertaken. In this paper we
provide some headway into settling the issue. For some classical algorithms, we demonstrate how
the use of readily available tools can lead to the correct assessment of complexity, suggesting that
the empirical approach can be feasible in practice.

KEYWORDS. Empirical analysis. Algorithms. Asymptotic complexity.

TAG – Theory and Algorithms on Graphs

https://proceedings.science/p/85400?lang=pt-br
1. Introdução
Diversos problemas do mundo real podem ser modelados através de grafos. Do ponto
de vista algorı́tmico, o principal desafio é desenvolver algoritmos eficientes, quando possı́vel, para
resolver tais problemas. É notório que a elaboração de algoritmos eficientes em grafos é, em geral,
uma tarefa difı́cil. Com efeito, uma quantidade expressiva de exemplos de problemas algorı́tmicos
intratáveis está relacionada a grafos. A análise de complexidade de algoritmos visa determinar a
quantidade de cada recurso requerido por um algoritmo em função da entrada. Dentre tais recursos
de interesse, estão o tempo, medido através do número de passos, e o espaço, medido através da
quantidade de células alocadas de memória. É possı́vel empreender este estudo através de uma
abordagem analı́tica, aplicando-se técnicas especı́ficas de contagem de passos de execução (ou da
contagem de outro recurso de interesse) a partir da descrição do algoritmo. O resultado de tal
análise deve determinar uma expressão matemática em função dos dados de entrada que descreve
o consumo de um recurso requerido por um algoritmo, que é chamada complexidade do algoritmo.
Em geral, tal expressão é fornecida empregando-se a notação O e famı́lia, que expressam o seu
comportamento assintótico. Também pode-se proceder tal análise por uma abordagem empı́rica,
analisando-se a execução propriamente dita do algoritmo sob diversas entradas com o uso de ferra-
mentas ou algoritmos especı́ficos para medição dos recursos consumidos. No entanto, esta última
abordagem não produz, em geral, a expressão matemática que especifica o consumo de recursos.
Ao invés, ela é utilizada principalmente para a avaliação comparativa de diversos algoritmos que
resolvem um mesmo problema submetidos a um mesmo conjunto de entradas ou, ainda, para a
verificação do desempenho do algoritmo sob instâncias de entrada consideradas usuais para o pro-
blema prático em questão.
Neste trabalho, propomos o método da abordagem empı́rica também para a obtenção da
complexidade assintótica. Para ilustrar a sua aplicação, apresentamos um estudo de caso de análise
empı́rica automatizada de dois algoritmos clássicos: floresta geradora mı́nima e multiplicação de
matrizes. Este último, apesar de não ser um algoritmo em grafos, é utilizado como auxı́lio em di-
versos problemas desta classe. Como ilustração, podemos citar o problema de obter o número de
caminhos distintos entre cada par de vértices de um grafo, para o qual uma das abordagens reduz o
problema àquele de multiplicação de matrizes. Essa análise será conduzida com o auxı́lio de uma
ferramenta chamada EMA [Oliveira, 2017]. Além disso, apresentamos a metodologia desta ferra-
menta, ou mais especificamente, como ela obtém uma função a partir do conjunto das execuções
empı́ricas. Para justificar tal escolha, pesquisamos as ferramentas para análise automatizada de
algoritmos existentes na literatura e comparamos o EMA com outra ferramenta chamada RAML,
única com desenvolvimento ativo, além do próprio EMA, de acordo com a literatura. A comparação
foi feita tanto em termos das caracterı́sticas gerais, quanto em termos práticos, analisando os resul-
tados de ambas quando aplicadas ao algoritmo de busca em profundidade em grafos.
O trabalho está estruturado da seguinte maneira. Na Seção 2, revisamos as ferramentas
de análise automatizada encontradas na literatura. Na Seção 3, descrevemos a metodologia do
EMA. Na Seção 4, utilizamos o algoritmo de busca em profundidade em grafos para contrastar os
resultados produzidos pelo EMA e pelo RAML. Na Seção 5, obtemos as complexidades assintóticas
de algoritmos para dois problemas distintos utilizando-se o EMA como auxı́lio. Finalmente, na
Seção 6, resumimos os resultados obtidos e apontamos trabalhos futuros de interesse.
2. Análise Empı́rica e Ferramentas Automatizadas
A análise empı́rica de algoritmos em grafos encontra aplicação em diversos cenários.
Dentre eles, podemos citar: (i) para a comparação entre a complexidade obtida via método analı́tico
e aquela medida na prática [Fahad et al., 2014; Moret e Shapiro, 1995], que pode ser desejável
no uso didático ou para conferir se o método analı́tico e/ou a implementação do algoritmo foram
conduzidas corretamente; (ii) quando não se tem acesso ao código-fonte com a implementação de
um algoritmo e ainda assim é desejável determinar sua complexidade; (iii) quando a complexidade
é desconhecida analiticamente, pela dificuldade da matemática envolvida no algoritmo especı́fico;

https://proceedings.science/p/85400?lang=pt-br
(iv) para o auxı́lio na escolha do algoritmo que possui função com menor constante multiplica-
tiva quando há mais de um algoritmo de mesma complexidade; (v) quando é necessário prever a
quantidade de tempo/memória necessários para execução do algoritmo para entradas as quais o al-
goritmo ainda não foi submetido e deseja-se prever uma quantidade numérica aproximada de qual
será o consumo de recursos, observando-se tais consumos para entradas já utilizadas em execuções
anteriores; entre outras aplicações.

Tabela 1: Ferramentas para análise automatizada de algoritmos.


Programa Desenvolvimento
Ferramenta Ano Complexidade Abordagem Disponı́vel
de Entrada Ativo
Melhor, pior
METRIC 1975 LISP Empı́rica Não Não
e caso médio
Linguagens
ACE 1988 Pior caso Analı́tica Não Não
funcionais
λΥΩ 1988 Caso médio Proprietário Analı́tica Não Não
Estilo
ACME 1998 Caso médio Analı́tica Não Não
Pascal
Pior caso Estilo
ANAC 2001 Analı́tica Não Não
e caso médio Pascal
Trend
2007 Pior caso C Empı́rica Não Sim
Profiler
Melhor, pior
Aprof 2012 C Empı́rica Não Sim
e caso médio
Função custo
AlgoProf 2012 Java Empı́rica Não Sim
real esperada
Pior caso
MOCCA 2014 Python Empı́rica Não Não
e caso médio
RAML 2012 Pior caso OCaml Analı́tica Sim Sim
Melhor, pior
EMA 2015 Qualquer Empı́rica Sim Sim
e caso médio

Embora a pesquisa de ferramentas que produzem a complexidade assintótica de algorit-


mos via método empı́rico não seja recente, há poucas ferramentas disponı́veis. A Tabela 1 resume
as ferramentas para análise automatizada de algoritmos encontradas na literatura. É possı́vel ob-
servar que a tentativa de desenvolver ferramentas com este objetivo ocorre desde a década de 70
através do METRIC [Wegbreit, 1975], que foi pioneira neste assunto e motivou as diversas fer-
ramentas que surgiram nos anos seguintes. Ferramentas como ACE [Le Métayer, 1988], λΥΩ
[Flajolet et al., 1989], ACME [Silveira, 1998], ANAC [Barbosa et al., 2001] e RAML [Hoffmann
et al., 2017] fornecem a complexidade de algoritmos através da análise do código-fonte, ao passo
que o METRIC [Wegbreit, 1975], Trend Profiler [Goldsmith et al., 2007], Aprof [Coppa et al.,
2012], AlgoProf [Zaparanuks e Hauswirth, 2012], MOCCA [Costa et al., 2014] e EMA [Oliveira,
2017] são ferramentas que analisam um algoritmo a partir de sua execução. Além disso, é possı́vel
observar que a maior parte dessas ferramentas possuem muitas limitações, tais como análise de
algoritmos implementados em um determinado paradigma de programação e em uma linguagem
de programação especı́fica. Além disso, apenas as ferramentas Trend Profiler, Aprof, AlgoProf,
RAML e EMA estão disponibilizadas para uso. Algumas dessas ferramentas fornecem apenas a

https://proceedings.science/p/85400?lang=pt-br
complexidade de pior caso, enquanto outras fornecem a complexidade de caso médio, ou ambas.
Dentre todas as ferramentas apresentadas, apenas o RAML e o EMA estão em desenvolvimento
ativo. O EMA analisa algoritmos escritos em qualquer linguagem de programação, enquanto o
RAML apenas algoritmos escritos na linguagem OCaml.

3. Metodologia do EMA
O EMA (acrônimo de EMpirical Analysis of algorithms) [Oliveira, 2017] é uma ferra-
menta com o objetivo de fazer análise empı́rica de um algoritmo de forma automática. O algoritmo
é executado para diversas entradas e, para cada uma, o EMA mede e armazena a quantidade con-
sumida dos recursos sendo monitorados. A partir das medições coletadas, o EMA sugere a função
que representa o consumo de cada recurso de acordo com sua metodologia. Os recursos padrões
monitorados são tempo e espaço, mas outros recursos especı́ficos de usuário podem ser definidos e
monitorados.
A entrada do EMA consiste em três dados básicos: (i) um programa executável A a ser
analisado; (ii) uma lista V de variáveis v1 , . . . , vn das quais a complexidade de A dependa; (iii)
um programa executável B que gera entradas para o programa A; a entrada de B consiste em uma
lista de valores a1 , . . . , an que corresponde à valoração de v1 , . . . , vn associada a entrada de A a
ser gerada por B. Por exemplo, se A for uma busca em profundidade em grafos de complexidade
de tempo O(n + m), v1 poderia corresponder a n e v2 a m, respectivamente o número de vértices
e arestas do grafo de entrada, pois estas são as variáveis das quais a complexidade depende. Neste
caso, B deve ser um programa que recebe um par de valores (n, m) e gere uma entrada que repre-
sente para A um grafo com n vértices e m arestas. Note que o requerimento de B é a forma que
o EMA dispõe para gerar entradas para qualquer programa a ser analisado sem ter que conhecer as
especificidades de como formatar uma entrada para ele. Ao iniciar uma execução de A com uma
entrada gerada com o auxı́lio de B para certa valoração de V , o EMA monitora o consumo de recur-
sos associados e, ao término, armazena tal consumo associado àquela valoração. O processamento
do EMA consiste em três etapas, brevemente descritas a seguir: calibração, simulação e análise.
A calibração consiste do EMA sugerir uma lista de valores de cada variável em V para
as quais o programa A deverá executar. Por exemplo, no exemplo em que A é uma busca em
profundidade, a calibração poderia resultar em um conjunto de valores de n = 300, . . . , 1000 e
m = 2000, 3000, . . . , 10000. Esta sugestão é feita através de uma série de experimentos que o
EMA conduz, com diversas entradas. O maior valor na lista sugerida de cada variável constitui
a maior instância para a qual A executou sem que o limite máximo de algum recurso tenha sido
violado. No exemplo, portanto, a maior instância de entrada que a busca em profundidade conseguiu
executar com os limites impostos pelo usuário de tempo e espaço foi um grafo tal que n = 1000 e
m = 10000.
A simulação consiste em executar A para os diversos valores de variáveis escolhidos na
calibração. Cada execução é feita para uma valoração de V distinta dentre todas as combinações que
podem ser feitas com valores especı́ficos de cada variável. Formalmente, se Si é a lista de valores
sugeridos para a variável vi retornada pela calibração, a simulação executará A para cada valoração
(a1 , . . . , an ) ∈ Q
S1 × . . . × Sn . Portanto, na base de dados com o consumo das execuções serão
inseridas L = ni=1 |Si | entradas. No exemplo anterior, a simulação da busca em profundidade
conduzirá L = |S1 ||S2 | = 8 × 9 = 72 execuções, uma para cada combinação dos valores de n e m.
A análise de determinado recurso consiste em obter a função que melhor se ajusta a
um conjunto de pontos D = {(xi , yi ) : 1 ≤ i ≤ L} onde para cada 1 ≤ i ≤ L, xi é a
valoração especı́fica de uma variável associada a uma instância de entrada e yi é o consumo do
recurso sob análise na execução de tal entrada. Tal conjunto D é obtido como resultado da fase
de simulação. A “função que melhor se ajusta” na metodologia do EMA é alguma da classe
logarithmico-exponential [Graham et al., 1994], que são as funções polilogarı́tmicas, polinomiais,
exponenciais, ou alguma combinação multiplicativas destas, tipicamente encontradas nas análises

https://proceedings.science/p/85400?lang=pt-br
de complexidade. Como será mostrado a seguir, o EMA considerará então um conjunto bem-
definido de tipos de função parametrizáveis, uma métrica de erro para qualificar uma função es-
pecı́fica, o conceito de funções equivalentes (conjunto de funções cujos erros estão muito próximos
dois a dois dentro de um limite), e por fim um critério para escolher uma função equivalente como
a mais provável que representa o consumo de recurso. A metodologia geral do EMA está expressa
através do fluxograma da Figura 1. A função
a2
xa3 (log2 x)a4 +a5
fgeral (x) = a0 ax1 |{z}
|{z} | {z }
t1 t2 t3

é aquela considerada a função mais geral que o EMA supõe descrever a complexidade de um al-
goritmo, onde a1 , . . . , a5 são os parâmetros cujos valores serão determinados pelo EMA. Neste
função, t1 , t2 , t3 são os seus termos. Para determinar os valores dos parâmetros e quais deles são re-
levantes para definir a função que representa uma complexidade, define-se o conjunto F de funções
candidatas. Tais funções são funções mais particulares que fgeral formadas por todas as combinações
possı́veis entre os termos desta função [Figura 1(a)]. A seguir, enquanto F 6= ∅, o EMA determi-
nará os parâmetros de cada f ∈ F [Figura 1(b)] por regressões não-lineares através do algoritmo de
Levenberg-Marquardt [Levenberg, 1944; Marquardt, 1963], que consiste um algoritmo numérico
que determinar o valor dos parâmetros que minimiza o erro associado a tal função. O erro erro(f )
de uma função f é dado por
L
X
erro(f ) = e2i (f ),
i=1

onde ei (f ) é o erro residual do ponto (xi , yi ) ∈ D, definido por

ei (f ) = f (xi ) − yi .

O EMA utiliza um conjunto de técnicas para auxiliar a regressão que contorna problemas
conhecidos do método de regressão computacional utilizado [Figura 1(c)]. São elas: (i) a estimação
dos parâmetros de cada função f é feita em sua forma original f (y = f (x)) e em escala logarı́tmica
flog (log y = log f (x)). Esta segunda forma é particularmente útil quando o método numérico ca-
minha por valores muito grandes que potencialmente ultrapassariam o limite de representação com-
putacional da biblioteca utilizada para fazer a regressão; (ii) o método numérico necessita que sejam
informados os valores iniciais para os parâmetros. É sabido que se tais valores estiverem em ordens
de magnitude afastados dos valores ótimos globais dos parâmetros, a qualidade de ajuste pode ser
prejudicada pois pode-se convergir para ótimos locais. Devido a isso, o EMA então testa um con-
junto de valores iniciais de ordens de magnitude diferentes para aumentar a chance de obter o ótimo
global; (iii) o método numérico é sensı́vel a se os valores dos parâmetros que são ótimos globais são
de ordens de magnitude diferentes. Neste caso, o método pode convergir prematuramente. O EMA
detecta e soluciona este tipo de problema. Ao final, o EMA armazena em um conjunto Fajustadas a
função ajustada gmin de menor erro encontrada como o ajuste final de f [Figura 1(d)].
Os parâmetros das equações são encontrados através de um método numérico, portanto,
frequentemente os valores encontrados são não-inteiros. Por outro lado, é muito comum que as
funções reais que medem recursos computacionais possuam como valores dos parâmetros determi-
nados números especiais, como os inteiros. Por esse motivo, o próximo passo do EMA é discretizar
os parâmetros que consiste de testar alguns de tais valores especiais na vizinhança do valor não-
inteiro encontrado, criando novas funções, que entram na lista de funções candidatas [Figura 1(e)].
Finalmente, o EMA reporta Fajustadas com seus elementos classificados em três grupos
[Figura 1(f)]: (i) função de erro mı́nimo fmin = argmin{erro(f ) : f ∈ Fajustadas }; (ii) funções
equivalentes Fequiv , que são aquelas com erro próximo a erro(fmin ); e (iii) função melhor-palpite

https://proceedings.science/p/85400?lang=pt-br
Figura 1: Metodologia da etapa de análise do EMA.

fmelhor-palpite , que é aquela que o EMA escolhe dentre as equivalentes e que é reportada como a com-
plexidade do algoritmo. Para elegê-la, o EMA utiliza o critério da Navalha de Occam, que consiste
em selecionar a função mais simples. Uma função é dita ser mais simples que outra quando ela é
considerada mais frequente na literatura de algoritmos. Mais especificamente, uma função é mais
simples que outra quando: (a) possui menos parâmetros livres (com valores a serem determinados);
se iguais, então aquela que (b) possui o menor número de termos (cada função tem de 0 a 3 termos);
se iguais, (c) possui menos parâmetros (sejam eles fixos ou variáveis); se iguais, então aquela que
(d) possui o maior número de parâmetros com valores inteiros.
4. Estudo de Caso: RAML vs. EMA
Nesta seção é apresentada uma comparação entre as ferramentas EMA e RAML (acrônimo
de Resource Aware ML) [Hoffmann et al., 2017]. Dentre todas as ferramentas, são as únicas com o
projeto de pesquisa ainda ativos. O RAML determina uma função polinomial de grau no máximo
6 que limita superiormente o uso de recursos de algoritmos escritos na linguagem OCaml. Deste
modo, já se nota uma diferença entre as ferramentas, pois o EMA considera funções com ter-
mos multiplicativos que podem ser exponenciais, polinomiais ou polilogarı́tmicos, o que o habilita
medir com precisão a complexidade do MergeSort de Θ(n log n), por exemplo, tarefa que não é
possı́vel para o RAML. Por outro lado, o RAML executa de maneira quase imediata, por fazer
análise do código-fonte, enquanto o EMA deve proceder a simulação sob várias entradas, o que
requer mais tempo. Inicialmente, foi realizada uma análise automatizada para entradas de pior caso
do QuickSort através das duas ferramentas. Para execução deste experimento, foi utilizado uma
implementação recursiva e, para que fosse simulada para grandes quantidades de elementos sem
estouro de pilha, um tipo especı́fico de recursão precisou ser implementada, chamada recursão de
cauda. A complexidade de pior caso encontrada para o QuickSort através do EMA foi Θ(N 2 ). A
expressão obtida pelo RAML foi de O(N 3 ), um limite que não é justo. Além disso, foi realizada

https://proceedings.science/p/85400?lang=pt-br
no RAML uma análise do QuickSort em sua versão sem cauda e, para esta versão, o RAML obteve
o limite justo de O(N 2 ). Isto evidencia que o RAML é sensı́vel a forma de escrita de um algoritmo
mesmo que ambos sejam de mesma complexidade.
No segundo estudo foi utilizado o algoritmo clássico de busca em profundidade, cuja
complexidade é de Θ(m+n), onde m representa o número de arestas e n aquele de vértices. O EMA
reporta a complexidade assintótica apenas de uma variável por análise, por isso, foram realizados
dois experimentos. Em ambos, as arestas foram determinadas aleatoriamente. No primeiro, foi
fixado n = 15 000 e variado o valor de m. Portanto, a complexidade do algoritmo esperada é
Θ(m), obtida da expressão geral considerando-se n como constante. No segundo experimento, foi
fixado m = 42 497 e variado o valor de n. A complexidade empı́rica esperada é Θ(n) por raciocı́nio
análogo. As Figuras 2 e 3 mostram o resultado reportado pelo EMA nas duas análises. O RAML
não foi capaz de analisar este algoritmo.

0.085777672199⋅m1 0.100272856838⋅n1
35000 13500

13000

30000
12500

12000
25000

11500
Tempo (ms)

Tempo (ms)

20000 11000

10500

15000
10000

9500
10000

9000

5000 8500
0 50000 100000 150000 200000 250000 300000 350000 0 5000 10000 15000 20000 25000 30000 35000 40000 45000
m n
média estimativa média estimativa

Figura 2: Análise da busca em profundidade Figura 3: Análise da busca em profundidade


com n = 15 000. com m = 42 497.

5. Estudo de Caso: Análise Empı́rica Aplicada a Algoritmos em Grafos Clássicos


Nesta seção, aplicaremos a análise empı́rica para a determinação da complexidade as-
sintótica de dois algoritmos clássicos: floresta geradora mı́nima e multiplicação de matrizes. As
subseções seguintes introduzem os respectivos problemas e apresentam os resultados.
5.1. Floresta Geradora Mı́nima
Um grafo G é conexo se existe caminho conectando cada par de vértices de G. Um grafo
G é acı́clico se G não possuir ciclos. Uma floresta é um grafo acı́clico. Uma floresta geradora de
um grafo G é uma floresta F tal que V (F ) = V (G) e E(F ) ⊆ E(G). Seja G um grafo com peso
w(e) em cada aresta e ∈ E(G). A floresta geradora mı́nima de G é uma floresta geradora F de G
de menor peso w(F ) dentre todas as florestas geradoras de G, onde
X
w(F ) = w(e) .
e∈E(F )

https://proceedings.science/p/85400?lang=pt-br
A Figura 4 ilustra uma floresta geradora mı́nima de um dado grafo. As arestas em negrito são
aquelas pertencentes à floresta geradora mı́nima.

Figura 4: Exemplo de um grafo e uma floresta geradora mı́nima deste grafo.

O problema de determinar uma floresta geradora mı́nima é uma extensão do problema


clássico de encontrar uma árvore geradora mı́nima. A extensão está em permitir que o grafo de
entrada seja desconexo. Um algoritmo clássico para o problema de árvores geradoras mı́nimas é o
algoritmo de Kruskal, de complexidade O(m log n), apresentado no Algoritmo 1. O algoritmo é
diretamente aplicado para obter florestas geradoras mı́nimas, sem alterações. Contudo, sua comple-
xidade se torna O(m log n + n). Isto se deve ao fato de que o algoritmo é de tempo Ω(n) por conta
da criação da lista de adjacências do grafo. No caso de grafos conexos, este termo desaparece pois,
para eles, vale que n = O(m) (e, portanto, n é dominado por O(m log n) na expressão assintótica).

Algoritmo 1 Algoritmo de Kruskal


Entrada: Grafo G
Saı́da: Uma floresta geradora mı́nima de G

1: função K RUSKAL(G)
2: F ← (V (G), ∅)
3: E ← O RDENAR(E(G)) . ascendentemente por peso
4: para cada e ∈ E faça
5: se “E(F ) ∪ {e} é acı́clico” então
6: E(F ) ← E(F ) ∪ {e}
retornar F

Para se chegar a complexidade de O(m log n+n), é necessário que a condição da Linha 5
seja refinada de modo que seja implementada em tempo O(log n). Para tanto, este refinamento
se faz com o uso da estrutura de dados U NI ÃO D ISJUNTA, que implementa a união de conjuntos
disjuntos e o teste se dois elementos estão no mesmo conjunto em tempo O(log n) [Cormen et al.,
2009]. Mais especificamente, a ideia é manter os vértices de cada componente conexa de F em
um mesmo conjunto, distinto do conjunto associado aos vértices de outras componentes. Assim,
referente a Linha 2, inicializa-se a estrutura com n conjuntos, cada um com um vértice distinto. A
Linha 5 é implementada verificando-se se os conjuntos associados aos vértices de e são distintos.
Em caso positivo, a Linha 6 une tais conjuntos. Uma análise mais precisa da complexidade da
U NI ÃO D ISJUNTA é feita via análise amortizada, cuja prova é não-trivial e utiliza a bem-conhecida
função de Ackermann [Cormen et al., 2009]. A escolha deste algoritmo para este estudo de caso se
deve ao fato de ser um algoritmo clássico cuja análise de complexidade de tempo através do método
analı́tico não é direta. Esta última caracterı́stica está presente em geral nos algoritmos candidatos a
serem submetidos à análise empı́rica.
Foram realizados dois experimentos para se verificar empiricamente a complexidade as-
sintótica do algoritmo. Em ambos os experimentos, as arestas e pesos dos grafos foram determi-
nados aleatoriamente. No primeiro, foi fixado n = 15 000 e variado o valor de m. Neste caso, a
complexidade do algoritmo esperada é O(m), que é obtida da expressão geral considerando-se n
como constante. A Figura 5 mostra o resultado reportado pelo EMA deste experimento.

https://proceedings.science/p/85400?lang=pt-br
No segundo experimento, foi fixado m = 42 497 e variado o valor de n. A complexi-
dade empı́rica esperada é O(n). A Figura 6 mostra o gráfico e a função reportada pelo EMA. Note
que, em uma análise superficial, a complexidade teórica deste experimento poderia ser erronea-
mente considerada como O(log n), fazendo m fixo na complexidade bem conhecida do algoritmo
de Kruskal para árvores. Contudo, a análise empı́rica evidenciaria tal equı́voco.

−6 1 1
7.86394418437⋅10 ⋅m 0.01668756406⋅n
250 800

700

200
600

500
Tempo (segundos)

150

Tempo (ms)
400

100
300

200
50

100

0 0
0⋅100 5⋅106 1⋅107 2⋅107 2⋅107 2⋅107 3⋅107 0⋅100 5⋅103 1⋅104 2⋅104 2⋅104 2⋅104 3⋅104 4⋅104 4⋅104 5⋅104
m n
std dev média estimativa std dev média estimativa

Figura 5: Análise de Kruskal com n = 15 000, Figura 6: Análise de Kruskal com m = 42 497,
mı́nimo de 10 amostras por valor de m. mı́nimo de 10 amostras por valor de m.

5.2. Multiplicação de Matrizes


A multiplicação de matrizes é utilizada por diversos problemas em grafos. Como exem-
plos, podemos citar a determinação do caminho mais curto (e o número de caminhos distintos) entre
todos os pares de vértices e o fechamento transitivo de um digrafo. Em especial, este problema tem
um interessante histórico por baixas sucessivas em sua complexidade, onde a cada novo algoritmo, a
melhora se dava em termos de décimos ou centésimos no expoente do polinômio [Williams, 2012].
Nesta seção, obtemos a complexidade assintótica empiricamente de dois algoritmos. O primeiro,
implementa diretamente a multiplicação de matrizes a partir de sua definição (que chamaremos de
direto). O segundo é conhecido como algoritmo de Strassen [Strassen, 1969], e foi o primeiro a
conseguir obter uma complexidade menor que aquela do algoritmo direto. Mais especificamente,
para matrizes A, B de dimensão N × N , a matriz R = A × B é aquela de dimensão N × N tal que
N
X
R[i, j] = A[i, k] × B[k, j], para todo 1 ≤ i, j ≤ N.
k=1

Naturalmente, a definição de multiplicação de matrizes é mais geral que a fornecida. A restrição


da multiplicação a matrizes quadradas é conveniente para que a complexidade dependa de uma
só variável. A complexidade do algoritmo que decorre desta definição é Θ(N 3 ). A análise de
complexidade deste algoritmo através do EMA é fornecida pelo gráfico da Figura 7.
O algoritmo de Strassen é assintoticamente de menor complexidade. Ele utiliza a técnica
de divisão e conquista da seguinte forma. Primeiro, particiona-se as matrizes A e B em quatro

https://proceedings.science/p/85400?lang=pt-br
submatrizes de dimensão N/2 × N/2 como esquematizado em (1). Em seguida, determina-se os
valores pi para todo 1 ≤ i ≤ 7 tal que a multiplicação matricial envolvida no cálculo de cada pi é
determinada recursivamente (fase de divisão). A ideia do método consiste na observação de que, a
partir destes valores, é possı́vel obter a matriz R = A × B conforme o esquema (fase de conquista).

    
A11 A12 B11 B12 p5 + p4 − p2 + p6 p1 + p2
= , (1)
A21 A22 B21 B22 p3 + p4 p1 + p5 − p3 − p7

A B R

onde:
p1 = A11 (B12 − B22 ); p2 = (A11 + A12 )B22 ; p3 = (A21 + A22 )B11 ;
p4 = A22 (B21 − B11 ); p5 = (A11 + A22 )(B11 + B22 );
p6 = (A12 − A22 )(B21 + B22 ); p7 = (A11 − A21 )(B11 + B12 ).

Deste modo, são feitas 7 multiplicações de matrizes de dimensão N/2 × N/2. A soma de
duas matrizes N × N é efetuada em tempo Θ(N 2 ). Assim, se T (N ) corresponde a complexidade
do algoritmo para multiplicação de matrizes N × N , T (N ) pode ser descrita pela equação de
recorrência T (N ) = 7T (N/2) + Θ(N 2 ) se N > 1, e T (1) = Θ(1), cuja resolução resulta em
T (N ) = Θ(N log2 7 ) ≈ Θ(N 2.8074 ). A Figura 8 apresenta a execução do algoritmo de Strassen
com a análise do EMA. É possı́vel observar que a complexidade encontrada pelo EMA é muito
próxima da complexidade teórica, com erro na segunda casa decimal do expoente do polinômio.

9.66100679144⋅10−5⋅N3 2.79406786377⋅10−11⋅N2.79296603768
30000 120

25000 100

20000 80
Tempo (horas)
Tempo (ms)

15000 60

10000 40

5000 20

0 0
200 250 300 350 400 450 500 550 600 650 700 0 5000 10000 15000 20000 25000 30000 35000
N N
média estimativa média estimativa

Figura 7: Análise da multiplicação de matrizes Figura 8: Análise da multiplicação de matrizes


N × N pelo algoritmo direto. N × N pelo algoritmo de Strassen.

6. Conclusão
Desenvolver algoritmos eficientes em grafos para problemas do mundo real é particular-
mente um desafio. Não raro, tanto a tarefa de determinar a complexidade de um algoritmo em

https://proceedings.science/p/85400?lang=pt-br
grafo, quanto a implementação propriamente dita em uma linguagem de programação, são tarefas
não-triviais. Para auxiliar o êxito das mesmas, neste trabalho, destacamos a importância da análise
empı́rica de algoritmos. Para a primeira, é uma forma direta de sugerir ou comprovar uma complexi-
dade obtida pelo abordagem analı́tica. Para a segunda, é uma forma de verificar se a implementação
seguiu rigorosamente as hipóteses feitas durante a etapa de análise. Neste trabalho, destacamos a
importância da análise empı́rica de algoritmos, conveniente em diversas outras situações.
Desde a década de 70, várias ferramentas para análise automatizada de algoritmos foram
desenvolvidas. A maior parte dessas ferramentas, sumarizadas na Seção 2, possuem limitações
por analisarem apenas algoritmos escritos em uma única linguagem de programação, em apenas
um paradigma de programação ou fornecer somente um tipo de função de complexidade. Dentre
todas, apenas EMA e RAML continuam com pesquisa ativa e, entre ambas, o EMA diferencia-
se por realizar estimativas de complexidade assintótica de algoritmos em diversas linguagens de
programação, em paradigma funcional e imperativo, e abordar diversas classes de complexidade
(polilogarı́tmica, polinomial e exponencial), cuja metodologia está sumarizada na Seção 3.
Na Seção 4, o EMA foi comparado com o RAML através de dois estudos de caso. No
primeiro, foi feita uma análise de pior caso do QuickSort. Devido a necessidade de gerar entradas
suficientemente grandes para a execução do EMA, ocasionando estouro de pilha, o QuickSort foi
implementado através de recursão de cauda. O EMA encontrou de maneira justa o resultado da
complexidade analı́tica que é Θ(N 2 ) ao passo que o RAML encontrou um limite superior não-justo
de O(N 3 ). Para a versão do QuickSort que não utiliza recursão de cauda, o RAML encontrou o
limite justo O(N 2 ). No segundo estudo de caso foi feita uma análise de pior caso do algoritmo
busca em profundidade, que possui complexidade Θ(m + n). Para tal experimento no EMA, foi
fixada uma variável em uma constante e variou-se a outra e obtivemos Θ(m) (para n fixo) e Θ(n)
(para m fixo), portanto, complexidades coerentes com aquela obtida pela abordagem analı́tica. O
RAML não foi capaz de analisar este algoritmo.
Por fim, na Seção 5, foram realizados dois estudos de caso. No primeiro, foi apresen-
tado o problema da floresta geradora mı́nima e realizada a análise de complexidade empı́rica do
algoritmo de Kruskal. É sabido que a complexidade deste algoritmo é O(m log n) para árvores e
O(m log n + n) para florestas. A análise empı́rica obteve êxito na determinação de tal comple-
xidade. Ressaltamos que apesar do algoritmo de Kruskal ser clássico e sua complexidade bem
conhecida, o fato de o usarmos de forma inalterada para florestas poderia criar a falsa expectativa
que sua complexidade é a mesma, o que valoriza o resultado da análise empı́rica. No segundo
estudo, foi realizada a análise de complexidade de dois algoritmos de multiplicação de matrizes:
aquele que decorre da definição de multiplicação matricial e o algoritmo de Strassen, cujas análises
empı́ricas foram de Θ(N 3 ) e Θ(N 2.79 ), respectivamente, que condizem com o método analı́tico.
Como trabalhos futuros, propomos a análise de complexidade assintótica de outros al-
goritmos em grafos através da abordagem empı́rica, a fim de entender mais aprofundadamente as
condições de sucesso e as limitações de tal abordagem.
Referências
Barbosa, M. A. C., Toscani, L. V., Ribeiro, L. (2001). Uma ferramenta para análise automática da
complexidade de algoritmos. Revista do CCEI, 5:57–65.

Coppa, E., Demetrescu, C., Finocchi, I. (2012). Input-sensitive profiling. ACM SIGPLAN Notices,
47:89–98.

Cormen, T. H., Leiserson, C. E., Rivest, R. L., Stein, C. (2009). Introduction to Algorithms. The
MIT Press, London, England, 3.a edição.

Costa, E. J., Ramos, J. G., Barbosa, Y. M., Filho, G. F., Brito, A. (2014). Um avaliador automático
de eficiência de algoritmos para ambientes educacionais de ensino de programação. Anais da 5.a
Computer on the Beach, p. 11–21.

https://proceedings.science/p/85400?lang=pt-br
Fahad, A., Alshatri, N., Tari, Z., Alamri, A., Khalil, I., Zomaya, A. Y., Foufou, S., Bouras, A.
(2014). A survey of clustering algorithms for big data: Taxonomy and empirical analysis. IEEE
Transactions on Emerging Topics in Computing, 2:267–279.

Flajolet, P., Salvy, B., Zimmermann, P. (1989). Lambda-Upsilon-Omega: an assistant algorithms


analyzer. Lecture Notes in Computer Science, 357:201–212.

Goldsmith, S. F., Aiken, A. S., Wilkerson, D. S. (2007). Measuring empirical computational com-
plexity. Anais da 6.a Joint Meeting of the European Software Engineering Conference and the
ACM SIGSOFT Symposium on The Foundations of Software Engineering, p. 395–404.

Graham, R. L., Knuth, D. E., Patashnik, O. (1994). Concrete Mathematics: a Foundation for
Computer Science. Addison-Wesley Professional, Boston, USA, 2.a edição.

Hoffmann, J., Das, A., Hofmann, M., Ngo, C., Shao, Z., Weng, S.-C. (2017). Resource Aware ML.
URL http://raml.co. Acesso em 03 de Março de 2018.

Le Métayer, D. (1988). ACE: an automatic complexity evaluator. ACM Transactions on Program-


ming Languages and Systems, 10:248–266.

Levenberg, K. (1944). A method for the solution of certain non-linear problems in least squares.
Quarterly of Applied Mathematics, 2:164–168.

Marquardt, D. W. (1963). An algorithm for least-squares estimation of nonlinear parameters. Jour-


nal of The Society for Industrial and Applied Mathematics, 11:431–441.

Moret, B. M. Shapiro, H. D. (1995). An empirical analysis of algorithms for constructing a mini-


mum spanning tree. Lecture Notes in Computer Science, 519:400–411.

Oliveira, F. S. (2017). EMA - webpage. URL http://fabianooliveira.ime.uerj.br/


ema. Acesso em 20 de Março de 2017.

Silveira, C. M. (1998). Analisador de complexidade média baseado nas estruturas algorı́tmicas.


Dissertação de Mestrado, UFPEL, Pelotas.

Strassen, V. (1969). Gaussian elimination is not optimal. Numerische Mathematik, 13:354–356.

Wegbreit, B. (1975). Mechanical program analysis. Communications of the ACM, 18:528–539.

Williams, V. V. (2012). Multiplying matrices faster than Coppersmith-Winograd. Anais da 44.a


Annual ACM Symposium on Theory of Computing (STOC), p. 887–898.

Zaparanuks, D. Hauswirth, M. (2012). Algorithmic profiling. ACM SIGPLAN Notices, 47:67–76.

https://proceedings.science/p/85400?lang=pt-br
Powered by TCPDF (www.tcpdf.org)

Você também pode gostar