Prof. Rodrigo L. S. Silva, D.Sc. Prof. Stênio Sã R. F. Soares, D.Sc.
Análise de Algoritmos
não Recursivos
O que é análise de algoritmos?
Analisar um algoritmo significa prever os recursos
de que o algoritmo necessitará.
Por que é interessante analisar algoritmos?
Frequentemente o tempo de computação de um
algoritmo é o que se deseja medir.
Para que serve a análise de algoritmos?
Pela análise de vários algoritmos candidatos para
um mesmo problema, pode-se identificar facilmente qual dos candidatos é o mais eficiente.
2
Itens a serem considerados ao analisar um algoritmo:
Tamanho da entrada: geralmente é medida pelo
número de itens a serem processados
Tempo de execução: número de operações
primitivas ou passos executados. Vamos considerar
que toda linha i a ser executada levará um tempo ci,
onde ci é uma constante.
O custo total de cada linha será somado para obtermos uma expressão T(n), que represente o tempo de execução de um determinado algoritmo.
3
Estudo de caso – Insertion-Sort
Algoritmo que resolve o problema de ordenação
Entrada: Um sequência de n números inteiros <a1, a2,...,an>
Saída: Reordenação da sequência de entrada <a’1, a’2,...,a’n> tal que a’i ≤ a’i+1
4
Para cada elemento k, obtém-se o ponto de inserção p do valor na porção do arranjo já ordenada.
O ponto de inserção p é caraterizado da seguinte forma:
elementos com índice menores que p são menores
que k
elementos com índice maiores do que p são maiores
que k
Repete-se o processo até que todos os elementos sejam ordenados.
Ordenação por inserção
Ordenação por inserção
6
Código do Algoritmo
void insertionSort(int V[], int tam) {
int i, j, aux;
for(i = 1; i < tam; i++) { for(j = i; V[j] < V[j - 1] && j!=0; j--) { aux = V[j]; V[j] = V[j - 1]; V[j - 1] = aux; } } }
Ordenação por inserção
7
void insertionSort(int V[], int tam) {
int i, j, aux;
for(i = 1; i < tam; i++) { for(j = i; V[j] < V[j - 1] && j!=0; j--) { aux = V[j]; V[j] = V[j - 1]; V[j - 1] = aux; } imprimeVetor(v); } }
Quais seriam as saídas do procedimento “imprimeVetor”
Solução
Ordenação por inserção
8 i / índice 0 1 2 3 4 5 1 2 5 4 6 1 3 2 2 4 5 6 1 3 3 2 4 5 6 1 3 4 1 2 4 5 6 3 5 1 2 3 4 5 6
O tempo despendido pelo procedimento Insertion-sort depende da entrada.
Quanto maior for a entrada de dados, maior será o tempo de execução.
Porém, o algoritmo pode demorar períodos diferentes de tempo para duas sequências de entrada de mesmo tamanho...
Ordenação por inserção
Ordenação por inserção
10 custo vezes c1 c2 c3Considere ti como o número de vezes que o teste do laço interno é executado para
cada valor de i. O custo total T(n) é:
Neste caso, ti varia de acordo com as características dos elementos do arranjo.
i t )* n ( 1 ) t ( )* n ( 1 i 1 n c n c ( n )*t c (n )(t ) T 1 2 1 i 3 1 i 1
Custo/número de vezes de cada instrução
void insertionSort(int V[], int tam) {
int i, j, aux;
for(i = 1; i < tam; i++) { for(j = i; V[j] < V[j - 1] && j!=0; j--) { aux = V[j]; V[j] = V[j - 1]; V[j - 1] = aux; } } } n
Melhor Caso: Números já ordenados
ti = 1, conteúdo do laço interno não será executado:
11
n an b
T
Pior Caso: Números em ordem inversa
ti = i:
n an bn c
T 2
Fazendo ci = 1
12
Ordenação por inserção
Em geral damos mais atenção à descoberta do tempo de execução do pior caso, ou seja, o tempo de execução mais longo para qualquer entrada de tamanho n.
Por questão de simplicidade, nos exemplos a seguir serão omitidos os custos cn de cada linha.
13
n i i x 0 Analisaremos o tempo de processamento de um programa para calcular o somatório (série
geométrica):
Somatório de série geométrica
SomaSerieGeometrica1(int x, int n) {
int soma = 0;
for(int i=0; i<=n; i++){ int prod = 1;
for(int j=0; j<i; j++) prod = prod*x;
soma = soma + prod; }
return soma; }
14
n i i x 0 Analisaremos o tempo de processamento de um programa para calcular o somatório (série
geométrica):
Somatório de série geométrica
SomaSerieGeometrica1(int x, int n) {
int soma = 0;
for(int i=0; i<=n; i++){ int prod = 1;
for(int j=0; j<i; j++) prod = prod*x;
soma = soma + prod; } return soma; } 1 n+2 n+1 n+1 1 ) / ) n (( )* n ( 1 2 2 2 15 5 2 n n n T ) / ) n (( )* n ( 1 1 2
15 x x x x x x x x n n i i
1 1 1 1 1 2 0 Regra de HornerSomatório de série geométrica
SomaSerieGeometrica2(int x, int n) {
int soma = 0;
for(int i=0; i<=n; i++){ soma = soma*x +1;
}
return soma; }
16 x x x x x x x x n n i i
1 1 1 1 1 2 0 Regra de HornerSomatório de série geométrica
SomaSerieGeometrica2(int x, int n) {
int soma = 0;
for(int i=0; i<=n; i++){ soma = soma*x +1; } return soma; } Vezes 1 n+2 n+1 1 n 2n5 T
17 1 1 1 0
xx x n n i i Fórmula fechadaSomatório de série geométrica
SomaSerieGeometrica3(int x, int n) {
return (pot(x,n+1) – 1)/(x – 1); }
Função para potência: pot(int x, int n) com complexidade T(n) = log2(n+1) + 2
n log2n 12
18
Comparação
Somatório de série geométrica
n log2n 12 T n 2n5 T Programa Soma 1 Soma 2 Soma 3 0 5 10 15 20 0 50 100 150 200 250 300 soma1 soma2 soma3 n t 2 15 5 2 n n n T
19
Análise assintótica
Estudo do comportamento de algoritmos com
entradas suficientemente grandes
Ordem de crescimento do tempo de execução em
função do tamanho da entrada
Em geral, um algoritmo assintoticamente mais eficiente é sempre considerado melhor que um algoritmo assintoticamente menos eficiente para entradas de dados suficientemente grandes.
Notação assintótica
Notações assintóticas
Notações utilizadas para descrever o tempo de
execução assintótico de algoritmos
Exemplo das principais notações assintóticas:
O(n): em geral, pior caso (lido como “O de n”)
(n): em geral, melhor caso (lido como “ômega de n”)
(n): ordem de crescimento (ou caso médio) (lido como
“theta de n”)
Notação assintótica
Notações assintóticas
Notações utilizadas para descrever o tempo de
execução assintótico de algoritmos
Exemplo das principais notações assintóticas:
O(n): em geral, pior caso (lido como “O de n”)
(n): em geral, melhor caso (lido como “ômega de n”)
(n): ordem de crescimento (ou caso médio) (lido como
“theta de n”)
Notação assintótica
Dada função g(n)
O(g(n)) é um conjunto de funções
O(g(n)) = {f(n) : existem constantes positivas c e n0
tais que 0 f(n) cg(n) para todo n n0}
Notação O oferece um limite assintótico superior para f(n) quando a empregamos para limitar o tempo de execução do pior caso de um algoritmo.
Notação O
Atividade
Suponha três trechos de programas cujos tempos de execução são O(n), O(n2) e O(n log n).
Qual o tempo de execução final?
Justifique.
Atividade
Suponha três trechos de programas cujos tempos de execução são O(n), O(n2) e O(n log n).
Qual o tempo de execução final?
Justifique.
Solução:
O tempo de execução dos dois primeiros é
O( max( n, n2 )), que é O( n2 ).
O tempo de execução dos três trechos é então:
O( max( n2 , n log n)), que é O( n2 ).
26
Notação Assintótica
Terminologia de classes mais comuns de funções:
Logarítmica - O(log n)
Linear - O(n)
Quadrática - O(n2)
Polinomial – O(nk), com k≥1
Exponencial – O(an), com a>1
26
Função Designação
c Constante log N Logaritmo
log2 N Logaritmo Quadrado
N Linear N log N N log N
N2 Quadrática
N3 Cúbica
Exercícios
Informe o número de passos relativos dos trechos de código a seguir e, a partir desta informação, avalie a complexidade assintótica (pior caso) de cada um dos casos. Considere que operações aritméticas, atribuições e comparações são representadas por uma constante c = 1.
Copie o código e insira contadores para verificar se o número de passos encontrado está correto de acordo com um certo valor n de entrada.
Exercícios
sum = 0;
for( i=0; i<n; i++ ) sum++;
Exercícios (solução)
sum = 0;
for( i=0; i<n; i++ ) sum++; (1) Vezes 1 n+1 n n 2n 2 T Complexidade O(n)
Exercícios (solução)
sum = 0;
for( i=0; i<n; i++ ) sum++; (1) Vezes 1 n+1 n n 2n 2 T void contaPassos(int n) {
int i, sum, cont = 0; // variáveis auxiliares que não estavam no problema
sum = 0;
cont++;
for( i=0; cont++ && i<n; i++)
{
sum++;
cont++;
}
printf("Tamanho da base: %d\n", n);
printf("Número de passos esperados: %d\n", 2*n+2); printf("Número de passos obtidos: %d\n", cont);
}
Código para contar o número de passos do algoritmo
Complexidade O(n)
Exercícios
sum = 0;
for( i=0; i<n; i++ )
for( j=0; j<n; j++ ) sum++;
sum = 0;
for( i=0; i<n; i++ )
for( j=0; j<n*n; j++ ) sum++;
sum = 0;
for( i=0; i<n; i++ )
for( j=0; j<i; j++ ) sum++;
(2)
(3)
Exercícios
(5) Considere a, b e prod matrizes quadradas
com dimensão n x n. for (i = 0; i < n; i++) { for (j=0; j < n; j++) { prod[i][j] = 0; for (k = 0; k < n; k++) prod[i][j]=prod[i][j]+a[i][k]*b[k][j]; } }
Exercícios
Utilizando a notação O, justifique a complexidade assintótica de pior caso para os seguintes problemas:
Encontrar o menor elemento em um vetor de
tamanho n.
Calcular a soma de todos os elementos de uma
matriz quadrada de ordem n.
Calcular a soma de todos os elementos da diagonal
principal de uma matriz quadrada de ordem n.
Verificar se um valor de entrada num está ou não
Leitura obrigatória
Livro do Cormen (Algoritmos)
Parte I
Capítulo 2 (Conceitos Básicos)