Introdução a complexidade de algoritmos

Download Report

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,…