ferramentas de análise
DESCRIPTION
Ferramentas de AnáliseTRANSCRIPT
ANÁLISE DE ALGORITMOS – AULA 1
Página 1 de 11
FERRAMENTAS DE ANÁLISE
1 INTRODUÇÃO
As estruturas de dados e os algoritmos são conceitos centrais para a computação, pois
se constituem nas ferramentas básicas para a construção de softwares. De forma simples, uma
estrutura de dados é a forma pela qual a informação é organizada e armazenada, e um
algoritmo é um procedimento, passo a passo, para realizar alguma tarefa em tempo finito.
O quadro-resumo a seguir exibe as etapas na construção de um programa
computacional, inserindo as estruturas de dados e os algoritmos no processo de solução de
um problema.
MODELO MATEMÁTICO TIPOS ABSTRATOS DE DADOS
(TAD’s*) ESTRUTURAS DE DADOS
ALGORITMO INFORMAL PROGRAMA EM
PSEUDOLINGUAGEM
PROGRAMA
COMPUTACIONAL
∗ TAD é a descrição matemática dos valores que um tipo de dado pode ter e das operações que
podem ser feitas com eles.
Como se está interessado em bons algoritmos e estruturas de dados, é importante a
definição de ferramentas de análise. A ferramenta básica de análise envolve a caracterização
do tempo de execução de algoritmos e operações sobre estruturas de dados, com seu
consumo de memória também sendo de interesse.
2 TEMPO DE EXECUÇÃO
Se um algoritmo for implementado, é possível estudar o tempo gasto por ele
executando-o com vários dados de entrada e registrando o tempo gasto em cada execução.
Essas medições podem ser feitas de forma precisa usando-se chamadas do sistema que são
incluídas na linguagem ou no sistema operacional em que o algoritmo foi implementado.
Em geral, o interesse é determinar a dependência do tempo de execução com respeito
ao tamanho da entrada fornecida para o algoritmo. Para determiná-la, podem-se realizar
vários experimentos e, por meio de gráficos, relacionar o tamanho da entrada com o tempo de
execução medido. Para ser significativa, essa análise exige que sejam escolhidos bons
ANÁLISE DE ALGORITMOS – AULA 1
Página 2 de 11
exemplos de entradas e que sejam feitos testes suficientes para que se possam fazer
afirmações válidas sob o ponto de vista estatístico.
2.1 Fatores que afetam o tempo de execução
O tempo de execução de um algoritmo depende de uma série de fatores:
• Cresce com o tamanho da entrada, embora possa variar para entradas diferentes,
porém de mesmo tamanho.
• É afetado pelo hardware em que o algoritmo é executado (processador,
freqüência, memória, disco, etc.) e pelo software (sistema operacional, linguagem
de programação, compilador, interpretador, etc.) sobre o qual o algoritmo é
implementado, compilado e executado.
Se todos os outros fatores forem iguais, o tempo de execução do mesmo algoritmo para
os mesmos dados de entrada será menor se o computador tiver, por exemplo, um processador
muito mais rápido, ou se a implementação for feita como um programa escrito em código de
máquina em vez de uma implementação interpretada em uma máquina virtual.
2.2 Requisitos para uma metodologia geral de análise
Embora os estudos experimentais dos tempos de execução sejam úteis, eles têm três
limitações sérias:
• Os experimentos são feitos utilizando-se um número limitado de entradas de
teste, podendo não ser indicativos do tempo de execução com outras entradas
que não foram incluídas nos experimentos.
• É difícil comparar a eficiência de dois algoritmos, a não ser que os experimentos
para a obtenção de seus tempos de execução tenham sido feitos com o mesmo
conjunto de hardware e software.
• É necessário implementar e executar um algoritmo para poder estudar seu tempo
de execução experimentalmente.
Desta forma, é apresentada neste curso uma metodologia geral para analisar o tempo
de execução de um algoritmo, que:
• Leva em conta todas as entradas possíveis.
• Permite avaliar a eficiência relativa de dois algoritmos independentemente do
hardware e do software em que eles são implementados.
• Pode ser feita por meio do estudo de uma descrição de alto nível do algoritmo
(pseudocódigo) sem que seja necessário implementá-lo ou executá-lo.
ANÁLISE DE ALGORITMOS – AULA 1
Página 3 de 11
Essa metodologia objetiva associar a cada algoritmo uma função f(n) que caracteriza o
tempo de execução do algoritmo como uma função do tamanho n da entrada.
3 ANÁLISE DE ALGORITMOS
3.1 Operações primitivas
Como visto anteriormente, a análise experimental é importante, mas tem suas
limitações. Para analisar um algoritmo em particular sem realizar experimentos para medir seu
tempo de execução, pode-se usar a abordagem analítica. Esta abordagem consiste em analisar
diretamente o código de alto nível ou pseudocódigo.
Para isso, define-se um conjunto de operações primitivas de alto nível que são
independentes da linguagem de programação usada e podem se identificadas no
pseudocódigo. São exemplos de operações primitivas:
• Atribuição de valores a variáveis.
• Chamadas de métodos.
• Operações aritméticas.
• Comparação de dois números.
• Acesso a um arranjo.
• Seguir uma referência a um objeto.
• Retorno de um método.
Uma operação primitiva corresponde a uma instrução de baixo nível com um tempo de
execução constante, mas que depende do ambiente em termos de software e hardware.
Entretanto, nesta abordagem assume-se implicitamente que os tempos de execução de
operações primitivas diferentes são similares. Assim, o número t de operações primitivas que
um algoritmo realiza será proporcional ao tempo de execução desse algoritmo.
Considere-se, como exemplo, o algoritmo seguinte, que retorna o elemento máximo de
um arranjo A de n elementos.
Algoritmo maxVetor (A, n) Entrada: arranjo A com n≥0 elementos Saída: max, o maior elemento em A
max=A[0] % chute inicial para (i=1, i<n, i++) faça
se (A[i]>max) então max=A[i]
retorne (max)
ANÁLISE DE ALGORITMOS – AULA 1
Página 4 de 11
Número de operações primitivas:
• max=A[0]: duas operações primitivas (indexação de um arranjo e atribuição de
valor a uma variável).
• i=1, no laço: uma operação primitiva (atribuição de valor a uma variável).
• i<n: uma operação primitiva (comparação de dois números) multiplicada por n.
• Corpo do laço, que varia entre 4(n-1) e 6(n-1):
o A[i]>max: duas operações primitivas (indexação de um arranjo e
comparação de dois números).
o max=A[i]: duas operações primitivas (indexação de um arranjo e atribuição
de valor a uma variável), que só serão executadas se o teste anterior for
verdadeiro.
o i++: duas operações primitivas (soma e atribuição de valor a uma variável).
• retorne max: uma operação primitiva.
Resumindo:
• 2 + 1 + n + 4(n-1) + 1 = 5n, no mínimo.
• 2 + 1 + n + 6(n-1) + 1 = 7n – 2, no máximo.
O melhor caso (t(n)=5n) ocorre quando A[0] é o maior elemento e, portanto, a variável
max não tem seu valor alterado posteriormente. O pior caso (t(n)=7n-2) ocorre quando o
arranjo A tem seus elementos em ordem decrescente, de forma que max é alterada a cada
iteração do laço.
3.2 Análise de caso médio, melhor e de pior caso
Um algoritmo pode ser mais rápido para certas entradas do que para outras. Isto
significa que há situações em que o número de operações primitivas é mínimo (melhor caso) e
outras em que este número é máximo (pior caso).
É possível expressar o tempo de execução de um algoritmo como uma média calculada a
partir de todas as entradas possíveis (caso médio). Todavia, uma análise de caso médio requer
tipicamente que sejam calculados os tempos de execução baseados em uma distribuição de
probabilidade. Esse tipo de análise frequentemente exige matemática sofisticada e teoria das
probabilidades.
A figura seguinte exibe esquematicamente como o tempo de execução do caso médio
de um algoritmo pode ter qualquer valor entre o melhor caso e o pior caso, dependendo da
distribuição das entradas.
ANÁLISE DE ALGORITMOS – AULA 1
Página 5 de 11
A complexidade do pior caso é a mais utilizada porque sua análise é mais fácil do que a
análise do caso médio, já que não requer conhecimento sobre teoria das probabilidades (mas
isso não é uma regra geral). Requer apenas a habilidade de identificar a pior entrada possível,
o que geralmente é simples. Além disso, fornece um limite superior para o número de passos
que o algoritmo pode efetuar, o que pode conduzir a algoritmos melhores: tendo certeza de
que um algoritmo tem bom desempenho no pior caso garante que ele tem bom desempenho
em todos os casos.
4 NOTAÇÃO ASSINTÓTICA
Na análise de algoritmos, é importante concentra-se na taxa de crescimento do tempo
de execução como uma função do tamanho da entrada n, obtendo-se um quadro geral do
comportamento, em vez de concentra-se em detalhes menores. Frequentemente, basta saber
que o tempo de execução de um algoritmo como maxVetor cresce proporcionalmente a n.
Para simplificar a análise, somente o comportamento assintótico é avaliado, desconsiderando-
se constantes aditivas e multiplicativas na expressão matemática obtida.
Desta forma, caracteriza-se o tempo de execução e a quantidade de memória relativos a
um algoritmo usando funções que mapeiam números inteiros em números reais , de forma a
concentrar a atenção no comportamento geral do tempo de execução e da memória exigida.
4.1 Notação O
A notação assintótica foi inventada por P. Bachmann, em 1892, para caracterizar o
comportamento assintótico de funções. Esta notação ficou conhecida como notação O (big oh)
e é definida da seguinte forma:
ANÁLISE DE ALGORITMOS – AULA 1
Página 6 de 11
Definição: Sejam f(n) e g(n) funções mapeando inteiros não-negativos em números
reais. Dizemos que f(n) é O(g(n)) se existe uma constante real c>0 e uma constante inteira n0≥1
tais que f(n)≤cg(n) para todo inteiro n≥n0.
Esta definição é ilustrada na figura a seguir:
A notação O permite afirmar que uma função de n é assintoticamente “menor que ou
igual a” outra função (pela desigualdade ≤), descontando-se um fator constante (a constante c)
e à medida que n cresce para o infinito (condição n≥0). De outra forma pode-se dizer que a
notação O caracteriza o crescimento assintótico de uma função estabelecendo um limite
superior quanto à taxa de crescimento da função em relação ao crescimento de n.
A notação O é largamente usada para caracterizar o tempo de execução e o consumo de
memória em função de um parâmetro n que varia de problema para problema. A notação O
permite ignorarem-se fatores constantes e termos de menor ordem, centrando-se nos
componentes que mais afetam o crescimento de uma função.
A partir do algoritmo maxVetor, discutido anteriormente, pode-se dizer que o seu
tempo de execução, utilizando a notação assintótica, é O(n). De fato, a partir da expressão 7n-
2, que exprime o número máximo de operações primitivas, a relação f(n)≤cg(n) verifica, ou
seja: 7n-2 ≤ 7n, para c≥7 e para n0≥1.
Exemplos:
• 20n3 + 10n log n + 5 é O(n3). Justificativa: 20n
3 + 10n log n + 5 ≤ 35n3, para c=35 e
n≥1. De fato, qualquer polinômio da forma aknk + ak-1n
k-1 + ... + a0 será sempre
O(nk).
• 3log n + log log n é O(log n). Justificativa: 3log n + log log n ≤ 4log n, para c=4 e n≥2.
• 2100 é O(1). Justificativa: 2100 ≤ 2100, para c=2100 e n≥1.
ANÁLISE DE ALGORITMOS – AULA 1
Página 7 de 11
4.2 Notações ômega e theta
Da mesma forma que a notação O fornece um limite assintótico superior sobre uma
função, outras notações fornecem maneiras assintóticas de fazer outros tipos de comparações.
Definição: Sejam f(n) e g(n) funções mapeando números inteiros em números reais. Diz-
se que f(n) é Ω(g(n)) (dito f(n) é ômega de g(n)) se g(n) é O(f(n)); ou seja, se existe uma
constante c>0 e uma constante inteira n0≥1 tais que f(n)≥cg(n) para n≥n0.
Esta definição permite dizer que uma função é assintoticamente maior que ou igual a
outra, exceto por um fator constante, conforme ilustra o gráfico seguinte.
Exemplos:
• 20n3 + 10n log n + 5 é Ω(n3). Justificativa: 20n
3 + 10n log n + 5 ≥ 35n3, para c=35 e
n≥1. De fato, qualquer polinômio da forma aknk + ak-1n
k-1 + ... + a0 será sempre
Ω(nk).
• 3log n + log log n é Ω(log n). Justificativa: 3log n + log log n ≥ 3log n, para c=3 e n≥2.
• 7n-2 é Ω(n). Justificativa: se f(n) = 7n-2, a relação 7n-2 ≥ 5n verifica, para c=5 e n0=1
Da mesma que as notações O e Ω, a definição seguinte permite dizer que duas funções
são assintoticamente iguais, exceto por um fator constante.
Definição: Sejam f(n) e g(n) funções mapeando números inteiros em números reais. Diz-
se que f(n) é Ѳ(g(n)) (dito f(n) é theta de g(n)) se f(n) é O(g(n)) e f(n) é Ω(g(n)); ou seja, se
existem constantes c’>0 e c’’>0, e uma constante inteira n0≥1 tais que c’g(n)≤ f(n) ≤ c’’g(n) para
n≥n0.
Exemplos:
• 3log n + log log n é Ѳ(log n). Justificativa: 3log n + log log n ≥ 3log n, para c’’=3 e
n0=2; 3log n + log log n ≤ 4log n, para c’=4 e n0=2.
ANÁLISE DE ALGORITMOS – AULA 1
Página 8 de 11
5 ANÁLISE ASSINTÓTICA
A tabela seguinte lista as expressões de complexidade mais freqüentes, por ordem de
crescimento assintótico, da direita para a esquerda:
EXPRESSÃO NOME
1 Constante
log Logarítmica
(log ) log quadrado
√ Raiz quadrada
Linear
log n log n
Quadrática
Cúbica
2 Exponencial
Se existem supostamente dois algoritmos diferentes que resolvem o mesmo problema,
um algoritmo A e um algoritmo B, com tempos de execução O(n) e O(n2), respectivamente,
isso implica que o algoritmo A é assintoticamente melhor do que o algoritmo B, embora para
algum dado valor (pequeno) de n seja possível que B tenha um tempo de execução menor do
que o algoritmo A.
A diferença na taxa de crescimento das funções pode ser ilustrada pela tabela seguinte.
Por meio desta tabela é possível perceber a importância da análise de algoritmos para o
projeto de algoritmos.
log √ log 2
2 1 1,4 2 2 4 8 4
4 2 2 4 8 16 64 16
8 3 2,8 8 24 64 512 256
16 4 4 16 64 256 4.096 65.536
32 5 5,7 32 160 1.024 32.768 4.294.967.296
64 6 8 64 384 4.096 262.144 1,84 x 1019
128 7 11 128 896 16.384 2.097.152 3,40 x 1038
256 8 16 256 2.048 65.536 16.777.216 1,15 x 1077
ANÁLISE DE ALGORITMOS – AULA 1
Página 9 de 11
512 9 23 512 4.608 262.144 134.217.728 1,34 x 10154
1.024 10 32 1.024 10.240 1.048.576 1.073.741.824 1,79 x 10308
5.1 Exemplos de análise assintótica de algoritmos
5.1.1 Algoritmo de tempo quadrático para o problema das médias prefixadas
Algoritmo prefixAverages1 (X) Entrada: arranjo X com n≥1 elementos Saída: arranjo A com n elementos, tal que A[i] é a média de X[0] ... X[i] para (i=0; i<n; i++) faça b=0 para (j=0; j≤i; j++) faça b=b+X[j] A[i]=b/(i+1) retorne (A)
Análise de complexidade de tempo em qualquer caso: O(n2).
5.1.2 Algoritmo de tempo linear para o problema das médias prefixadas
Algoritmo prefixAverages2 (X) Entrada: arranjo X com n≥1 elementos
Saída: arranjo A com n elementos, tal que A[i] é a média de X[0] ... X[i] s=0 para (i=0; i<n; i++) faça s=s+X[i] A[i]=s/(i+1) retorne (A)
Análise de complexidade de tempo em qualquer caso: O(n).
5.1.3 Algoritmo de tempo cúbico para o cálculo do produto matricial
Algoritmo produtoMatricial (A, B) Entrada: arranjos A e B com nXn elementos, n>0 Saída: arranjo C com nXn elementos para (i=0; i<n; i++)faça para (j=0; j<n; j++) faça C[i, j]=0 para (k=0; k<n; k++) faça C[i, j]=A[i, k]*B[k, j]+C[i, j]
ANÁLISE DE ALGORITMOS – AULA 1
Página 10 de 11
retorne (C)
Análise de complexidade de tempo em qualquer caso: O(n3).
5.1.4 Algoritmo de ordenação por inserção
Algoritmo insertionSort (A) Entrada: arranjo A com n≥1 elementos Saída: arranjo A com n elementos para (j=1; j<n; j++) faça chave=A[j] i=j-1 enquanto (i≥0 e A[i]>chave) faça A[i+1]=A[i] i=i-1 A[i+1]=chave retorne (A)
Análise de complexidade de tempo do pior caso: O(n2).
Análise de complexidade de tempo do melhor caso: O(n).
Análise de complexidade de tempo do caso médio: O(n2).
EXERCÍCIOS
a) Ordene a lista de funções a seguir usando a notação O. Agrupe as funções que são Ѳ uma
da outra.
6 log 2 log log (log ) 2
2 5 , 1 ⁄ 4 ⁄
3, 2 (log ) 2 log
4 √ log 4 log
Dica: quando estiver em dúvida sobre duas funções f(n) e g(n), considere log (f(n)) e log
(g(n)), ou 2f(n) e 2g(n).
b) Faça um gráfico das funções 12, 6 log , , e 2, usando uma escala logarítmica
para os eixos x e y; ou seja, para o valor n e para a função f(n), “plote” a informação no
ponto (log n, log(f(n))).
c) Mostre que (n+1)5 é O(n5).
ANÁLISE DE ALGORITMOS – AULA 1
Página 11 de 11
d) O algoritmo A usa 10n log n operações enquanto que o algoritmo B usa n2 operações.
Determine o valor para n0 para o qual a é melhor do que B para n≥n0.
e) Mostre que as duas afirmações a seguir são equivalentes:
i. O tempo de execução do algoritmo A é O(f(n)).
ii. No pior caso, o tempo de execução do algoritmo A é O(f(n)).
f) Para cada par de funções f(n) e g(n) na tabela seguinte, indique se f(n)=O(g(n)) (caso 1), ou
se g(n)=O(f(n)) (caso2).
() ()
10 − 10
log
log + log
log √!
ln log
log( + 1) log
2 10
# $
cos( ' 2⁄ ) sin( ' 2⁄ )
( cos )