A apresentação está carregando. Por favor, espere

A apresentação está carregando. Por favor, espere

2. Conceitos básicos A complexidade de algoritmos trata do esforço computacional de algoritmos. Além disso, ela fornece subsídios para escolher o melhor.

Apresentações semelhantes


Apresentação em tema: "2. Conceitos básicos A complexidade de algoritmos trata do esforço computacional de algoritmos. Além disso, ela fornece subsídios para escolher o melhor."— Transcrição da apresentação:

1

2 2. Conceitos básicos A complexidade de algoritmos trata do esforço computacional de algoritmos. Além disso, ela fornece subsídios para escolher o melhor entre os algoritmos disponíveis para o problema. A complexidade em ordem assintótica avalia a complexidade do algoritmo quando o seu desempenho é mais crítico. Existem vários critérios de avaliação de um algoritmo como: quantidade de trabalho requerido, quantidade de espaço requerido, simplicidade, exatidão de resposta e otimalidade. Complexidade de Algoritmos

3 Visão geral do capítulo
2.1 Medidas de complexidade Introduz as idéias de complexidade pessimista (pior caso) e complexidade média, bem como as medidas de complexidade de tempo e de espaço. 2.2 Critérios de complexidade Introduz as idéias de operações fundamentais e de tamanho da entrada. Introduz também a idéia de comparação entre complexidades. 2.3 Ordens assintóticas Introduz as idéias de comportamento assintótico (notações O,  e ), e examina algumas de suas propriedades. 2.4 Exemplos ilustrativos Ilustram as idéias anteriores com alguns exemplos de algoritmos. Complexidade de Algoritmos

4 2.1 Ideias básicas 2.1.1 Complexidade e desempenho de algoritmos
medidas de tempo e espaço. 2.1.1 Complexidade e desempenho de algoritmos A complexidade de um algoritmo é o reflexo do esforço computacional requerido para executá-lo. Esforço computacional mede a quantidade de trabalho, em termos de tempo de execução ou da quantidade de memória requerida. Complexidade de Algoritmos

5 Aspectos a considerar na determinação da complexidade de um algoritmo: um critério pode ser medir o tempo de execução, mas: uma pequena mudança no programa pode não representar uma mudança significativa no algoritmo, e velocidade de execução ser bastante afetada; dois programas comparados, primeiro numa máquina, depois em outra podem dar resultados diferentes; este critério é fortemente dependente tanto da implementação quanto da máquina. Uma alternativa útil é uma análise matemática das dificuldades intrínsecas da resolução computacional do problema. Complexidade de Algoritmos

6 2.1.2 Medidas de desempenho e complexidade
Uma das medidas de desempenho mais importante é o tempo de execução: complexidade de tempo. Outra é a complexidade de espaço, que usa como medida de desempenho a quantidade de memória necessária para execução do algoritmo. Espaços: a área para guardar as instruções, as constantes, as variáveis e os dados. Se os dados têm uma representação óbvia como uma matriz, são considerados para a análise de espaço somente os espaços extras. A memória usada por um programa, bem como o tempo requerido na sua execução é dependente da implementação. Complexidade de Algoritmos

7 2.2 Critérios de complexidade
Para medir a quantidade de trabalho realizado por um algoritmo, é escolhida uma operação significativa do algoritmo, chamada operação fundamental. Às vezes, é necessária mais de uma operação fundamental, talvez com pesos diferentes. Por exemplo, para um algoritmo de ordenação, uma operação fundamental natural é a comparação entre elementos quanto à ordem. Custo: esforço computacional do algoritmo sobre essa entrada. É dado pelo número de operações fundamentais executadas pelo algoritmo. 2.2.1 Desempenho de algoritmo O desempenho (em tempo) de um algoritmo pode ser medido pelo custo de suas seqüências de execução. Complexidade de Algoritmos

8 Exemplo 2.2.1. Máximo de tabela Algoritmo 2.2.1: Máximo de tabela
Procedimento Max(tab : tabela) {para máximo de tabela} { Entrada: tabela tab : vetor [ 1..n ] de Elmord . Saída: Mx = valor máximo em tab.} 1. Mx  tab[1] ; {candidato a máximo} 2. para i de 2 até n faça {varre tabela: 2} 3. se Mx < tab[i] então Mx  tab[i] fim-se ; {atualiza Mx} 4. fim-para ; {2: tabela varrida} 5. retorne-saída ( Mx ) {retorna valor máximo} 6. fim-Procedimento {fim do algoritmo: máximo de tabela} Análise: operação fundamental é a comparação < (no se da linha 3) Quantas vezes essa linha é executada ? Complexidade de Algoritmos

9 Dado um algoritmo a, considere os conjuntos:
D, de seus possíveis dados de entrada, e E, de todas as sequências de execuções de operações fundamentais. E as seguintes funções: Execução exec[ a ] : D  E; exec[ a ]( d ) dá a seqüência de execuções de operações fundamentais para os dados d. Custo custo : E   +; custo( s ) dá o comprimento da sequência s  E, levando em conta os pesos das operações fundamentais. Desempenho desemp[a] : D   +; desemp[ a ]( d ) := custo( exec[ a ]( d ) ) dá o custo (em operações fundamentais). O desempenho de um algoritmo com uma entrada mede o custo da execução do algoritmo sobre essa entrada. Complexidade de Algoritmos

10 2.2.2 Complexidade de algoritmo
Note que a função desemp : D   + dá informação sobre cada entrada específica. O objetivo é associar um custo a cada tamanho de entrada, em vez de a cada entrada. Tamanho tam : D   ; onde tam ( d ) mede o tamanho da entrada d  D. Por exemplo: dfgdfgdfg para matrizes quadradas, tam(d) dá a ordem de d. para listas, tam(d) pode ser o comprimento de d. para um grafo, pode ser dado pelos número de vértices ou o número de arestas. para números a quantidade de caracteres (como bits) usada para codificá-los. Usando a função tam, pode-se condensar a informação dada pela função desemp[ a ] : D   + em uma função aval[ a ] :     +, de modo que para tam( d ) = n: aval[ a ]( n ) dá informação sobre desemp[ a ]( d ). Complexidade de Algoritmos

11 Qual avaliação devemos escolher?
Note que a função tam : D   em geral não é inversível: costuma haver várias entradas com mesmo tamanho. Além disso, um algoritmo pode apresentar desempenhos distintos sobre várias entradas, apesar de elas terem o mesmo tamanho. Qual avaliação devemos escolher? Aqui entrarão os critérios de complexidade, como o do pior caso e o do caso médio. Complexidade de Algoritmos

12 A determinação de aval[a](n)
Dn := { d  D / tam( d ) = n } é o conjunto das entradas com tamanho n. Em geral, a função desemp não é constante sobre o conjunto Dn. Dois critérios razoáveis são tomar o valor máximo ou o valor médio dentre esses. Complexidade Média A complexidade média (ou esperada) de um algoritmo a (notada cM[ a ] ou avalM[ a ]) é o valor médio de seus desempenhos sobre todas as entradas com tamanho n, usando-se como peso a probabilidade de ocorrência de cada entrada avalM[ a ] = onde prob( d ) dá a probabilidade de ocorrer a entrada d no conjunto Dn Complexidade de Algoritmos

13 Complexidade pessimista
A complexidade pessimista de um algoritmo fornece seu desempenho no pior caso: o pior desempenho que se pode esperar sobre todas as entradas com tamanho n. A complexidade pessimista (pior caso de um algoritmo a (notada cP[ a ] ou avalP[ a ]) é o valor máximo de seus desempenhos sobre todas as entradas com tamanho n: avalP[ a ]( n ) := Máx { desemp[ a ]( d )  /  d  Dn } Exemplo 2.2.2: Busca Sequencial O problema é procurar um valor (chave) em uma tabela tab para determinar se ch encontra-se ou não em tab. Aqui tab será um vetor de n elementos. Complexidade de Algoritmos

14 Algoritmo 2.2.2: Busca sequencial em tabela
Procedimento Busca_Seq(ch : Valor, tab : tabela) {busca sequencial em tabela} { Entrada: ch: Valor, tabela tab : vetor [ 1..n ] de Valor. Saída: achou, pos (achou = V se ch está em tab e tab[pos] = ch).} 1. i  0 ; achou  F ; { inicialização } 2. repita {varre tabela: 5} 3 i  i + 1 ; {incrementa índice} 4. se tab[i] = ch então achou  V  fim-se {compara com chave} 5. até achou ou  i = n ; {2: tabela varrida} 6. se achou então pos  i  fim-se ; {verifica se achou chave} 7. retorne-saída( achou, pos ) {retorna saída} 8. fim-Procedimento {fim do algoritmo: busca seqüencial em tabela} Complexidade de Algoritmos

15 Operação fundamental: a comparação = (no se da linha 4) e como tamanho da entrada o tamanho n de tab. Se ch encontra-se: na 1a posição, Busca_Seq faz 1 comparação na 2a posição, Busca_Seq faz 2 comparações na ka posição, Busca_Seq faz k comparações na na posição (ou não está em tab), Busca_Seq faz n comparações. Assim, a complexidade no pior caso é n (linear no tamanho n da entrada). Se todos os elementos da tabela forem igualmente prováveis como chave, então o número médio de comparações será: Complexidade de Algoritmos

16 No pior caso, o critério anterior dá
cP = [ a ]( n ) := Máx { desemp[a]( d )   + / tam( d ) = n }; enquanto o critério relaxado dá cP  [ a ]( n )  := Máx { desemp[a]( d )   + /tam( d ) ≤ n }. O critério relaxado dá uma cota superior para o anterior: cP = [ a]( n ) ≤ cP  [ a ]( n ) n   . Uma vantagem do critério relaxado é que é uma função monotônica: cP  [ a ]( m ) ≤ cP  [ a ]( n ) para m ≤ n. Os critérios de complexidade, entradas de tamanho exatamente n ou até n, costumam dar a mesma ordem em boa parte dos algoritmos, principalmente os eficientes. Esses critérios de complexidade dependem do custo e tamanho da entrada. Complexidade de Algoritmos

17 2.2.3 Comparação entre complexidades
Um problema pode ter mais de um algoritmo para resolvê-lo. Qual deles escolher? Fixada as operações fundamentais e função tamanho, a cada algoritmo a Î A temos: desempenho (sobre A) desempA : A  D   + A é o conjunto de algoritmos para um dado problema . A pode consistir de algoritmos de uma certa família ou ser o conjunto de todos os algoritmos possíveis (conhecidos ou não) para o problema . avaliação (sobre A) aval : A  IN   + Um algoritmo ótimo é um algoritmo o  A, tal que aval(o) é melhor do que aval(a) para todo a  A. No caso em que A consiste de todos os algoritmos para o problema , a complexidade aval(o) é a complexidade (intrínseca) do problema . Complexidade de Algoritmos

18 2.3 Ordens assintóticas A complexidade assintótica é definida pelo crescimento da complexidade para entradas suficientemente grandes. Algoritmo assintoticamente mais eficiente é melhor para todas as entradas, exceto para entradas relativamente pequenas. A complexidade exata costuma conter muita informação, resultando em uma função de difícil análise e manipulação, vamos simplificar. Exemplo: Considerando as funções f(n) = n + 4 e g(n) = n2; Para: n = 0, 1, 2, temos f(n)  g(n); mas para n  4, temos f(n) = n + 4  n + n = 2 . n  n2 = g(n). Complexidade de Algoritmos

19 2.3.1 Notação O A notação O define uma cota assintótica superior.
Define-se que f(n) é O(g(n)) se: para alguma constante c   + : c . g(n) é f(n)  c . g(n).), i. e. ( Ǝ c   + ) ( Ǝ n0    ) ( n  n0 ): f(n)  c . g(n). Para n suficientemente grande, g(n) é cota superior para f(n) com um fator constante Complexidade de Algoritmos

20 f(n) = 5 . n + 7 e g(n) = n2; para n  7, temos
Exemplo: f(n) = 5 . n + 7 e g(n) = n2; para n  7, temos f(n) = 5 . n + 7  5 . n + n  6 . n  6 . n2 = 6 . g(n). Assim, tomando n0 := 7 e c := 6, vemos que para todo n  n0, temos f(n)  c . g(n); logo f(n) é ( g(n) ). Pode-se determinar a ordem de um polinômio a partir de seu grau. Por exemplo, considerando o polinômio cúbico k(n) = n3 + 3 . n2 + 7 . n + 5, para n  1, temos Logo, k(n) = n3 + 3 . n2 + 7 . n + 5 ≤ c n3 , para n  1 Complexidade de Algoritmos

21 2.3.2 Notação  A notação  define uma cota assintótica inferior a menos de constantes. De maneira geral, define-se f(n) é  ( g(n) ) sse para alguma constante d   + : d . f(n) g(n)  d . f(n), i. e. ( Ǝ d   + ) ( Ǝ n0    ) (  n  n0 ): g(n)  d . f(n). Para valores de n sufucientemente grande g(n) é sempre menor ou igual a d . f(n). Complexidade de Algoritmos

22 Exemplo: f(n) = n3 e quadrática g(n) = 6 . n2 + 10. para n  4, temos
g(n) = 6 . n2 + 10  6 . n2 + 16  6 . n2 + n2  7 . n2  7 . n3 = 7 . f(n). Assim, com n0 := 4 e d := 7, vemos que para todo n  n0, temos g(n)  d . f(n); logo f(n) é ( g(n) ). Os algoritmos de ordem ( 1 ), ( n ), ( n2 ), etc. são polinomiais, isto é, têm complexidade limitada por um polinômio (complexidade polinomial). Já 2n é uma função não limitada por polinômio, polinômio i. e. não existe m tal que 2n seja ( nm ), m uma constante. Um algoritmo de ordem ( 2n ), ( 3n ), etc., é dito exponencial. Quando o algoritmo é polinomial no valor da entrada (quando a entrada é um número) ao invés do tamanho da entrada o algoritmo é dito pseudopolinomial. Complexidade de Algoritmos

23 2.3.3 Notação  A notação  define um limite assintótico exato, a menos de constantes. Em geral, diz-se f(n) é ( g(n) ) sse ( Ǝ c, d   + ) ( Ǝ n0    ) (  n  n0 ): c . g(n)  f(n) & f(n)  d . g(n). Para n sufucientemente grande o valor de f (n) está entre os produtos c . g(n) e d . g(n). Complexidade de Algoritmos

24 Exemplo: f(n) = 7n + 3, g(n) = 2n + 5
Para n3, f(n) = 7n + 3 ≤ 7n + n = 8n ≤ 8(2n + 5) = 8 g(n) Para n5, g(n) = 2n + 5 ≤ 2n + n = 3n ≤ 3(7n +3) = 3 f(n) n0 = 5 = max{3,5}, c = 3, d = 8. temos c . g(n)  f(n) e f(n)  d . g(n); logo f(n) é ( g(n) ). Se f(n) é ( g(n) ), então f(n) também é ( g(n) ) e ( g(n) ). Utiliza-se notações assintótico: para simplificar a notação. Por exemplo usado simplesmente log n, independente da base, pois a troca de base é simplesmente a multiplicação por uma constante para expressar estimativas de erro em aproximações. Por exemplo, no truncamento aproximação de Stirling Se f(n) está à esquerda de g(n), então f(n) = ( g(n) ), e g(n) não é ( f(n) ). Complexidade de Algoritmos

25 2.4 Exemplos Exemplo 2.4.1: Máximo de tabela.
Algoritmo 2.1: Máximo de tabela. Procedimento Max(tab: tabela) {máximo de tabela} {Entrada: tabela tab: vetor [ 1..n ] de Elmord.Saída: Mx = valor máximo em tab.} 1. Mx  tab[1]; {candidato a máximo} 2. para i de 2 até n faça {varre tabela: de 2 a 4} 3. se Mx ≤ tab[i] então Mx  tab[i]; {atualiza Mx} 4. fim-para; {2: i de 2 até n} 5. retorne-saída (Mx); {fim do algoritmo: máximo de tabela} 6. fim-Procedimento. O desempenho do algoritmo Max em qualquer caso é n - 1 comparações. Tomando como tamanho da entrada o tamanho n da tabela e como operação fundamental a comparação <, a complexidade (média, no pior ou no melhor caso) de Max é n - 1, isto é de ordem linear ( n ). Complexidade de Algoritmos

26 Exemplo 2.4.2: Busca Sequencial
Algoritmo 2.2: Busca sequencial em tabela. Procedimento BuscaSeq(ch: Valor, tab: tabela) {busca sequencial em tabela} {Entrada: ch: Valor, tabela tab: vetor [ 1..n ] de Valor.Saída: achou, pos.} 1. i  0; achou  F; {inicialização} 2. repita {varre tabela} 3. i i+1; {incrementa índice} 4. se tab[i] = ch então achou  V; {compara com chave} 5. até achou v i = n; {fim do repita de 2} 6. se achou então pos  i; {verifica se achou chave} 7. retorne-saída (achou, pos); {fim do algoritmo: busca seqüencial em tabela} 8. fim-Procedimento. Tomando como operação fundamental a comparação = e como tamanho da entrada o tamanho n de tab. O algoritmo tem complexidade no pior caso n: de ordem ( n ), complexidade média de ordem ( n ). Complexidade de Algoritmos

27 Exemplo 2.4.3: Particionamento
O algoritmo de particionamento é de a C. A. Hoare, utilizado no algoritmo Quicksort Dado um pivô, particionar os elementos de um vetor, de tal forma que no final: todos os elementos à direita do pivô serão maiores ou iguais a ele, os elementos à sua esquerda serão menores que ele. O pivô é o primeiro elemento do vetor. O algoritmo usa dois apontadores que caminham em direções opostas: Assim, aplicado à entrada O algoritmo deve fornecer como saída. Complexidade de Algoritmos

28 Algoritmo 2.3: Particionamento.
Procedimento Particionamento(A: vetor, m,k: Elmord) {particionar vetor} {Entrada: vetor A: vetor [ 1..n ] de Elmord, m, k. Saída: (j, A), o vetor A com os elementos rearranjados de tal forma que todos os elementos inferiores a A[m] estarão à esquerda da posição j.} 1. v  A[m]; i  m+1; j  k; {v é pivô; i apontador da esq.; j apontador da dir.} 2. repita {2 a 6: percorre a nas duas direções} 3. enqto A[i] < v faça i  i+1; {elem. da esq. é < pivô, move apontador} 4. enqto A[j]  v faça j  j -1; {elem. da dir. é  pivô, move apontador} 5. se i < j então troca(A[i] e A[j]) {A[i] maior e A[j] menor que pivô} 6. i  i + 1; 7. j  j - 1; 8. fim-se; 9. até i > j; {2: todo a foi percorrido} 10. A[m]  A[j]; A[j]  v; {na posição j está o elem. < pivô mais à esquerda} 11. retorne-saída (j, A); {fim do algoritmo: particionar vetor} 12. fim-Procedimento. Complexidade de Algoritmos

29 Exemplo 2.4.4. Intercalação de listas
A intercalação de duas listas d1 e d2, com comprimentos m1 e m2, pode ser feita com m1 + m2 - 1 comparações: desemp[ Intercl ]( d1 , d2  ) = compr( d1 ) + compr( d2 ) - 1. a) Tomarndo como tamanho da entrada a soma dos dois comprimentos tam( d1 , d2  ) := compr( d1 ) + compr( d2 ) temos cP=[ Intercl ]( n ) = n - 1 e cP[ Intercl ]( n ) = n - 1. b) Tomando como tamanho da entrada o máximo entre os dois comprimentos tam( d1 , d2  ) := Máx [ compr( d1 ), compr( d2 ) ], temos cP=[ Intercl ]( n ) ≤ 2 . n - 1 e cP[ Intercl ]( n ) = 2 . n - 1. Alteração da noção de tamanho afeta a complexidade de intercalação, porém, as complexidades associadas a cada noção estão relacionadas. Complexidade de Algoritmos

30 Exemplo 2.4.5: Multiplicação de matrizes
O produto C de uma matriz A, p x q, por outra B, q x r é O algoritmo imediato, vindo diretamente dessa definição, obtém cada elemento Cij por uma soma de q parcelas, cada uma delas obtida por uma multiplicação de 2 elementos. Considerando matrizes quadradas n x n, tomando n como tamanho da entrada. Operação fundamental: a multiplicação de elementos, o algoritmo obtém cada um dos n2 elementos da matriz produto n multiplicações de elementos, o que leva a uma complexidade de n2 . n = n3. Operações fundamentais soma e multiplicação de elementos, o algoritmo obtém cada um dos n2 elementos do produto C por n multiplicações e n - 1 somas.o que leva a uma complexidade de n2 . ( 2 . n - 1 ) = 2 . n3 - n2 operações. Alteração da noção de custo das operações fundamentais afetou a complexidade. Complexidade de Algoritmos

31 2.5 Resumo do capítulo O objetivo deste capítulo foi introduzir e ilustrar os conceitos básicos de complexidade (medidas e critérios) e de análise assintótica. As principais medidas de complexidade são tempo e espaço, relacionadas à velocidade e quantidade de memória. O desempenho de um algoritmo sobre uma entrada é o esforço (quantidade de trabalho) para executar o algoritmo com essa entrada. A complexidade é determinada com base em operações fundamentais e no tamanho da entrada. A complexidade pessimista (ou no pior caso) dá o valor máximo. Complexidade de Algoritmos

32 A complexidade média (ou esperada) dá o valor esperado: a média dos esforços, levando em conta a probabilidade de ocorrência de cada entrada. Análise matemática fornece a complexidade intrínseca do algoritmo. A complexidade assintótica dá o desempenho para entradas de tamanho grande. As principais notações de comportamento assintótico dão limites superior (O), inferior () e exato (), a menos de constantes multiplicativas. Algoritmos polinomiais têm complexidade ( nk ); os algoritmos com complexidade ( 2n ), ( 3n ), etc.,algoritmos exponenciais Complexidade de Algoritmos


Carregar ppt "2. Conceitos básicos A complexidade de algoritmos trata do esforço computacional de algoritmos. Além disso, ela fornece subsídios para escolher o melhor."

Apresentações semelhantes


Anúncios Google