Baixe o app para aproveitar ainda mais
Prévia do material em texto
Análise de Complexidade de Algoritmos Estrutura de Dados Prof. Carla Koike - CIC Porque Analisar um algoritmo? ● Para avaliar sua performance e comparar diferentes algoritmos ● Mas analisar o que? – tempo de execução e uso de memória – pior caso e caso típico, dependendo das entradas ● Análise de algoritmos compara ALGORITMOS e não programas Tipos de problemas na análise de algoritmos ● Análise de um algoritmo particular Qual é o custo de usar um dado algoritmo na solução de um problema específico? ● Características que devem ser investigadas: – número de vezes que cada parte do algoritmo deve ser executada (ou o tempo de execução) complexidade do tempo – quantidade de memória necessária complexidade de espaço Tipos de problemas na análise de algoritmos, cont. ● Análise de uma classe de algoritmos. Qual é o algoritmo de menor custo possível para resolver um problema particular? ● Toda uma família de algoritmos é investigada, e procura-se identificar um que seja o melhor possível. ● Colocam-se limites para a complexidade computacional dos algoritmos pertencentes à classe Executando um algoritmo ● A complexidade de um algoritmo pode ser analisada a partir de uma implementação executando em um computador. – A eficiência do programa depende da linguagem (compilada ou interpretada) – depende do sistema operacional – depende do hardware (quantidade de memória, velocidade do processador e arquitetura) ● útil nas comparações entre programas em máquinas específicas, logo não somente os custos do algoritmo são comparados mas também os custos não aparentes (alocação de memória, indexação, carga de arquivos, etc) Análise pelo modelo matemático ● Não depende do computador nem da implementação ● O custo das operações mais significativas deve ser especificado, e algumas operações são desprezadas. ● É possível analisar a complexidade do algoritmo dependendo dos dados de entrada Exemplo 1 ● Qual o custo do algoritmo? soma <- 0 para i de 0 até n faça soma <- soma + i ● Somente atribuições são operações relevantes: – duas atribuições (soma e i recebem zero) – duas atribuições a cada laço ( soma recebe soma + i, e i recebe i+1) – Total de atribuições: 2 + 2n ● Função que define a complexidade de tempo do algoritmo é g(n) = 2 + 2n Exemplo 2 ● Algoritmo: dividir elementos de uma matriz quadrada por uma constante k soma <- 0 para i de 0 ate n faça para j de 0 ate n faça a[i,j] <- a[i,j]/k ● Número de atribuições: – duas atribuições (soma e i recebem 0) – para o laço externo: uma atribuição (i recebe i+1), n vezes – para o laço interno: duas atribuições (a e j ), n2 vezes ● Complexidade de tempo: g(n) = 2 + n + 2n2 Análise da função complexidade ● Considere a função: – g(n) = 2 + n + 2n2 ● Crescimento dos termos em função de n: – para n= 1, g(n) = 2 + 1 + 2, todos os termos tem o mesmo peso – para n = 1000, g(n) = 2 + 1000 + 2000000, o último termo é dominante, portanto os dois primeiros podem ser desprezados O melhor, o médio e o pior caso ● Melhor caso: função definida pelo menor número de passos executados para qualquer instância de tamanho n. ● Pior caso: função definida pelo maior número de passos executados para qualquer instância de tamanho n. ● Caso médio: função definida pela média do número de passos executados para qualquer instância de tamanho n. O melhor caso, o caso médio e o pior caso Complexidade Assintótica ● Para pequenos valores de n, a maioria dos algoritmos não representa problemas ● Estuda-se a complexidade somente para grandes valores de n, e essa complexidade é chamada assintótica ● O comportamento assintótico de g(n) representa o limite do comportamento do custo quando n cresce Dominância assintótica ● Definição: Uma função f(n) domina assintoticamente outra função g(n) se existem duas constantes positivas c e m tais que, para n ≥ m, temos |g(n)| ≤ c x |f(n)|. Notação O-Grande ● Escrevemos g(n) é O(f(n)) ( ou ainda g(n) = O(f(n)) ) para expressar que f(n) domina assintoticamente g(n). Lê-se “g(n) é da ordem no máximo f(n)” ● Exemplo: – Sejam g(n) = (n + 1)2 e f(n) = n2. – As funções g(n) e f(n) dominam assintoticamente uma a outra, desde que |(n + 1)2| ≤ 4|n2| para n ≥ 1 e |(n+1)2| ≥ |n2| para n ≥ 0 Notação O-Grande, cont. ● Definição: – Uma função g(n) é O(f(n)) se existem duas constantes positivas c e m tais que |g(n)| ≤ |cf(n)|, para todo n ≥ m. ● Exemplos: – g(n) = (n + 1)2. – g(n) é O(n2), quando m = 1 e c = 4. – Isto porque (n + 1)2 ≤ 4n2 para n ≥ 1. Notação O-Grande, cont. ● g(n) = 3n3 + 2n2 + n é O(n3) – Basta mostrar que 3n3 + 2n2 + n ≤ 6n3, para n ≥ 0. – A função g(n) = 3n3 + 2n2 + n é também O(n4), entretanto esta afirmação é mais fraca do que dizer que g(n) é O(n3). ● g(n) = log 5 n é O(log n) n=10log10n log5n=log510 log10n log5n=log10 n×log510 Constante! Operações com O-grande ● O(f(n)+g(n)) = O(max(f(n),g(n)) – Suponha três trechos de programa com complexidades O(n), O(n2) e O(nlogn), – A complexidade dos três trechos é O(n2) Notação Ω ● Definição: – Uma função g(n) é Ω(f(n)) se existem duas constantes positivas c e m tais que |g(n)| ≥ |cf(n)|, para todo n ≥ m. ● Enquanto O(f(n)) oferece um limite superior para a taxa de crescimento de g(n), Ω(f(n)) seria um limite inferior ● Exemplo: – g(n) = 3n3 + 2n2 é Ω(n3) basta fazer c = 1, e então 3n3 + 2n2 ≥ n3 para n ≥ 0 Notação Ω, Limite Inferior Notação Θ ● Definição: – Uma função g(n) é Θ(f(n)) se existirem constantes positivas c1, c2 e m tais que 0 ≤ c1f(n) ≤ g(n) ≤ c2f(n), para todo n≥ m. ● A notação Θ é um limite assintótico firme (ou exato) de g(n) 21 Diferenciando O e Ω T(n) T(n) O(g(n)) constantes c,n0 > 0 tais que nn0, T(n) cg(n) cg(n) n0 T(n) Ω(g’(n)) constantes c’,n’0 > 0 tais que nn’0, T(n) c’g’(n) c’g’(n) n’0 Exercícios ● Encontre o número de passos e a complexidade dos seguintes algoritmos: para i de 0 a n-1 faça para j de 0 a n-1 faça a[i][j] = b[i][j] + c[i][j] para i de 0 a n-2 faça para j de i+1 a n-1 faça temp = a[i][j] a[i][j] = a[j][i] a[j][i]=temp Solução para i de 0 a n-1 faça para j de 0 a n-1 faça a[i][j] = b[i][j] + c[i][j] 1 atribuição feita uma única vez: i=0 1 subtração feita uma única vez: n-1 1 soma feita n vezes: i+1 1 atribuição feita n vezes: i= i+1 1 atribuição feita n vezes: j=0 1 soma feita n*n vezes: j+1 1 atribuição feita n*n vezes: j = j+1 1 soma feita n*n vezes: b[i][j]+c[i][j] 1 atribuição feita n*n vezes: a[i][j]= b[i][j]+c[i][j] 2+3n+4n2=O(n2) e Ω(n2) Solução para i de 0 a n-2 faça para j de i+1 a n-1 faça temp = a[i][j] a[i][j] = a[j][i] a[j][i]=temp 1 atribuição feita uma única vez: i=0 1 subtração feita uma única vez: n-2 1 soma feita n-1 vezes: i+1 1 atribuição feita n-1 vezes j=i+1 4 atribuições e uma soma dentro realizadas no laço j: j = j+1, temp = a[i][j], a[i][j] = a[j][i], a[j][i] = temp feitas (n-1)+(n-2)+(n-3)+...1 vezes, ou seja 5* (Σ n-k), k=1 até n-1, = 5*n2/2 - 5/2 2+2*(n-1)+2.5n2-2.5 2.5n2+2n-2.5 = O(n2) e Ω(n2) Exercícios ● Mostre que: – 2(n+a) = O(2n) ● Suponha que um algoritmo A e um algoritmo B, com funções de complexidade de temp a(n) = n2 – n + 549, e b(n) = 49n+49, respectivamente. Determine quais valores de n pertencentes ao conjunto dos números naturais para os quais A leva menos tempo para executar do que B. Análise de Complexidade de AlgoritmosEstrutura de Dados Profa. Carla Koike CIC Principais Classes de Problemas ● f(n) = O(1): complexidade constante ● f(n) = O(log n): complexidade logarítmica ● f(n) = O(n): complexidade linear ● f(n) = O(n log n): ene log de ene ● f(n) = O(n2): complexidade quadrática ● f(n) = O(n3): complexidade cúbica. ● f(n) = O(2n): complexidade exponencial ● f(n) = O(n!): complexidade fatorial Se 1 instrução = 1μs ... Exemplo: Caixeiro Viajante ● Um caixeiro viajante precisa visitar n cidades de tal forma que sua viagem inicie e termine em uma mesma cidade, e cada cidade deve ser visitada uma única vez. Supondo que sempre há uma rota entre duas cidades quaisquer, encontre a menor rota que o caixeiro viajante pode utilizar na sua viagem. ● Algoritmo simples: verificar todas as rotas e escolher a menor delas. Existe (n1)! rotas possíveis, e cada rota envolve n adições para determinar a distância total: n! total de adições! ● Computador realiza 109 adições por segundo, resolver esse problema para 50 cidades levaria 1045 séculos de cálculo! Algoritmos ● Algoritmo exponencial – tempo de execução tem função de complexidade O(cn); c > 1. ● Algoritmo polinomial: – tempo de execução tem função de complexidade O(p(n)), onde p(n) é um polinômio. ● Algoritmos exponenciais: simples variações de pesquisa exaustiva. ● Algoritmos polinomiais: obtidos mediante entendimento aprofundado da estrutura do problema. Mas, tudo depende de n... ● Existem valores de n onde um algoritmo exponencial é mais rápido que um polinomial: – f(n) = 2n e g(n) = n5, para n < 20, o algoritmo exponencial é mais rápido. Técnicas de Análise de Algoritmos ● Considerar memória infinita ● Não considerar o sistema operacional nem o compilador ● Analisar de preferência o algoritmo e não o programa, e levar em conta o tamanho das entradas ● Somente alguns comandos são considerados: atribuição, adição, multiplicação e comparação, e eles executam em um único passo de tempo Algumas dicas... ● A complexidade de um laço é igual ao número de comandos internos vezes o número de vezes que ele é executado ● Exemplo: – para i de 1 até n faça soma < soma+1 – 1 atribuição externa ao laço – comandos internos do laço: 1 atribuição, 1 soma, 1 incremento – laço é executado n vezes – Complexidade g(n) = 3n + 1, O(n) Algumas dicas ... ● Laços Aninhados: a complexidade de laços aninhados é o produto dos tamanhos dos laços ● Exemplo: – para i de 1 até n faça – para j de 1 até m faça – soma < soma + i + j – Fora dos laços: 1 atribuição – Dentro somente do laço i: 1 atribuição – Dentro dos dois laços: 1 atribuição e 2 somas – g(n) = 1 + n + 3(nm), O(nm) Algumas dicas ... ● Para uma seqüência de laços do algoritmo: – para i de 1 até n faça soma < soma+1 – para i de 1 até n faça – para j de 1 até n faça – soma < soma + i + j – A primeira parte é O(n) e a segunda parte é O(n2) – Portanto esse trecho de algoritmo é O(n2) Algumas dicas ... ● No caso de testes condicionais, a complexidade é a maior das duas partes do teste ● Exemplo: – Se teste = 1 então – para i de 1 até n faça soma < soma +i – senão – para i de 1 até n faça – para i de 1 até n faça – soma < soma + i + j ● “Se” é O(n) e “Então” é O(n2), portanto complexidade é O(n2) Algumas dicas ... ● Funções não recursivas – O tempo de execução de cada procedimento deve ser computador separadamente, um a um, iniciando com os procedimentos que não chamam outros procedimentos. – A seguir, avaliase os procedimentos que chamam os procedimentos que não chamam outros procedimentos, usando os tempos já avaliados – Continua sucessivamente até chegar ao programa principal. Algumas dicas ... ● Funções Recursivas – Associamos a complexidade da função recursiva uma equação de recorrência. – Uma equação de recorrência é uma equação ou inequação que descreve uma função em termos do seu valor para entradas menores. – Resolvendo a equação de recorrência é possível avaliar a classe do algoritmo – Existem técnicas para resolver equações de recorrência... Exercícios 1 ● Encontre a complexidade computacional para o seguinte laço: for (cnt1 =0,i=1;i<=n;i++) for (j=1;j<=n;j++) cnt++; Exercícios 2 ● Encontre a complexidade computacional para o seguinte algoritmo de ordenação: inteiro i,j,min,x inicio para i de 1 ate n1 faca inicio min = 1 para j de i+1 ate n faca se A[j] < A[min] entao min = j x = A[min] A[min] = A[i] A[i] = x fim fim Slide 1 Slide 2 Slide 3 Slide 4 Slide 5 Slide 6 Slide 7 Slide 8 Slide 9 Slide 10 Slide 11 Slide 12 Slide 13 Slide 14 Slide 15 Slide 16 Slide 17 Slide 18 Slide 19 Slide 20 Slide 21 Slide 22 Slide 23 Slide 24 Slide 25
Compartilhar