Transcript Introdução a complexidade de algoritmos
Algoritmos e Estrutura de Dados:
Uma pequena motivação
Luiz Gonzaga da Silveira Jr [email protected]
Cenário visionário
• • • • Suponha que os computadores fossem infinitamente rápidos, com memória infinita. Você precisaria estudar algoritmos?!
Me dê 2 razões...
• Demonstrar que o método de sua solução termina, e, • o faz com a resposta correta!
Se todos os métodos estivessem corretos, você escolheria o método mais fácil de implementar...
Mas o mundo perfeito não existe: velocidade infinita e memória grátis...
• • • • • •
Eficiência: Caso
Computador A: 1 bilhão de instruções/sec Computador B: 10 milhões de instruções/sec Conclusão: • CompA é 100x mais rápido do que CompB Programador mais ansioso do mundo: • Ordenação (inserção) 2n 2 no CompA instruções para ordenar n números Programador mais relaxado do mundo: • Ordenação (intercalação) 50 n log n instruções para ordenar n números no CompB Para ordenar 01 milhão de números: • • • CompA: ~2000 segundos CompB: ~100 segundos CompB executou 20x mais rápido do que o CompA
Alguns problemas
• • • • • • Ordenação Busca Armazenamento Compressão Transmissão Descompressão Parâmetros de qualidade • • Corretude (depuração) Desempenho (perfilação)
• • • • •
Ordenação
Ordenar buscar!
Exercício 1: Uma função que verifique se um vetor v[0..n 1] está em ordem crescente.
Exercício 2: Uma função que busque a ocorrência de um valor em um vetor v[0..n-1] Exercício 3: Uma função que ordene um vetor com N elementos.
Comparação http://cg.scs.carleton.ca/~morin/misc/sortalg/
Qual a complexidade destas funções para as soluções encontradas?!
•
Melhor caso?
•
Pior caso?
•
Caso médio?
Um pouco de noção de complexidade • • Ao ver uma expressão como n+10 ou n 2 +1, a maioria das pessoas pensa automaticamente em valores pequenos de n, valores próximos de zero. • Testar: para n=2, n=3,n=4, n=10, n=100 A análise de algoritmos faz exatamente o contrário:
ignora os valores pequenos
e concentra-se nos
valores enormes
de n.
•
Algumas funções
Observemos as seguintes funções: n 2 , (3/2)n 2 ,9999n 2 , n 2 /1000, n 2 +100n • • Quem cresce mais rápido?! (claro, para valores enormes de n): vamos experimentar!
Resposta: • Todas têm crescimentos equivalentes • Crescimento assintótico!
• Nessa “ matemática ” , as funções são classificadas em ORDENS.
• Funções de mesma ordem são ditas equivalentes.
• As funções acima pertencem a mesma ordem
Ordem O (Big-Oh)
• • • • Segundo Knuth, “ O ” maiúsculo.
trata-se do
ômicron
Definição: grego Dadas funções assintoticamente não-negativas f e g dizemos que f está na ordem O de g, e escrevemos f =
O
(g), se f(n)
≤
c · g(n) para
algum
c positivo , e para
todo
n suficientemente grande.
Em outras palavras, existe um número positivo c e um número N tais que f(n) ≤ c · g(n) para todo n maior que N. Exemplo: Se f(n) ≤ 9999 g(n) para todo n ≥ 1000 então f =
O
(g).
(Mas cuidado: a recíproca não é verdadeira!)
Exemplo
• • Dadas as funções: f(n) = (3/2)n 2 + (7/2)n – 4 e que g(n) = n 2 .
n 0 1 2 3 4 5 6 7 f(n) –4 1 9 20 34 51 71 94 g(n) 0 1 4 9 16 25 36 49 8 120 64 A tabela sugere que f(n) ≤ 2g(n) para n ≥ 6 e portanto parece que f(n) =
O
(g(n)).
Bubblesort: intuitivo, porém...!
bubbleSort( A
:
lista )
do
swapped := false
for each
i
in
0
to
length( A ) - 2
do: if
A[ i ] > A[ i + 1 ]
then
swap( A[ i ], A[ i + 1 ] ) swapped := true
end if end for while
swapped
end
Implementação Alternativa
bubbleSort( A : lista ) n := length( A ) - 1 do swapped := false n := n - 1 for each i in 0 to n do: if A[ i ] > A[ i + 1 ] then swap( A[ i ], A[ i + 1 ] ) swapped := true end end if end for while swapped Qual a diferença entre as duas implementações?
Análise de complexidade
• • • Para uma lista de n elementos • • • Pior caso: O(n 2 ) Melhor caso: O(n) Posição dos elementos na lista define eficiência do algoritmo Para grande quantidade de dados: ineficiente!
Na prática: • • Simples (entender e implementar) Aceitável para n pequeno!
Inserção
• • Analogia: cartas do baralho!
Funcionamento: mão esquerda vazia...cartas pra baixo, retira carta da mesa e vai inserindo 5 2 4 6 1 3 2 5 4 6 1 3 2 4 5 6 1 3 2 4 5 6 1 3 1 2 4 5 6 3 1 2 3 4 5 6 • Complexidade • • Melhor caso:O(n)!
Pior caso:O(n 2 )
Implementação
void
insercao
(int n, int v[]) { int j, i, x; for (j = 1; j < n; j++) { x = v[j]; for (i = j-1; i >= 0 && v[i] > x; --i) v[i+1] = v[i]; v[i+1] = x; } }
Algoritmo de seleção
void
selecao
(int n, int v[ ]) { int i, j, min, x; for (i = 0; i < n-1; ++i) { min = i; for (j = i+1; j < n; ++j) if (v[j] < v[min]) min = j; x = v[i]; v[i] = v[min]; v[min] = x; } } Complexidade: O(n 2 )
Quicksort
• • • • Inventado por C.A.R. Hoare , em 1962.
Estratégia: dividir e conquistar!
Idéia: Dividir a lista em 2 sublistas Atividade: • Pesquisar o funcionamento do algoritmo!
• • Implementar para um conjunto de valores inteiros contidos no site Medir tempo!
Complexidade na prática
• • Considerações: • Tamanho do conjunto • Considerar usar algoritmos mais eficientes para grandes conjuntos • Natureza dos dados (repetidos, já ordenados ou praticamente ordenados,…) Se 2 algoritmos tem mesma complexidade, qual utilizar?!
Busca
Problema
• • • • Determinar se um dado número está ou não está em um dado vetor ordenado.
Mais precisamente,
dado um número
x
e um vetor crescente índice
m
tal que
v[0..n-1],
encontrar um
v[m] == x. É claro que o problema pode não ter solução. Este é o caso, por exemplo, se o vetor é vazio, ou seja, se n vale 0. Ex: 111 222 444 444 555 555 666 888 888 888 999 999
Solução 1: óbvia e lenta
• Comecemos com um algoritmo simples e óbvio: A função abaixo recebe um número x e um vetor // crescente v[0..n-1]. Ela devolve um índice m // em 0..n-1 tal que v[m] == x. • Se tal m não existe, // a função devolve -1. int buscaSequencial (int x, int n, int v[]) { int m = 0; while (m < n && v[m] < x) ++m; if (m < n && v[m] == x) return m; else return -1; }
Busca seqüencial
• • Comecemos com um algoritmo simples e óbvio: // A função recebe um número x e um vetor // crescente v[0..n-1]. • • Ela devolve um índice m tal que v[m] == x. Se tal m não existe, a função devolve -1. } int buscaSequencial (int x, int n, int v[]) { int m = 0; while (m < n && v[m] < x) ++m; if (m < n && v[m] == x) return m; else return -1;
Busca binária
int buscaBinaria (int x, int n, int v[]) { int e, m, d; e = 0; d = n-1; while (e <= d) { m = (e + d)/2; if (v[m] == x) return m; if (v[m] < x) e = m + 1; else d = m - 1; } return -1; }
Info importante
Diminuição o espaço de busca!
Falamos sobre estruturas…
•
Listas, vetores…indistintamente
•
Qual o papel destas estruturas nos processos de ordenação e busca?!
Java: Vector, ArrayList , LinkedList,…