Preview only show first 10 pages with watermark. For full document please download

Aed 11

Aula de Algoritmo e Estrutura de Dados do Professor Patrick Pedreira.

   EMBED


Share

Transcript

Algoritmos e Estruturas de Dados Introdução à Complexidade de Algoritmos Prof. Patrick Pedreira Introdu ção à Complexidade de Algoritmos Introdução  Complexidade de Algoritmos  Um algoritmo tem algumas caracterí características que determinam a sua utilidade:  Reusabilidade: Reusabilidade: Propriedade de um algoritmo, ou de determinados trechos de um algoritmo, ser utilizado por outros algoritmos. Podemos fazer isso usando mó módulos.  Legibilidade: Grau de facilidade de compreensão de um algoritmo. O uso de mó módulos, endentaç endentação e comentá comentários ajudam na legibilidade  Correç Correção: Um algoritmo sempre deve executar a tarefa para o qual ele foi projetado de forma correta.  Eficiência: Propriedade de executar a tarefa da melhor maneira. A definiç definição do que é esta “melhor maneira” maneira” envolve a aná análise de alguns parâmetros, como utilizaç utilização de recursos da má máquina e tempo de resposta. 1 Introdu ção à Complexidade de Algoritmos Introdução Na análise de desempenho, centrada no tempo de resposta, temos duas abordagens:  Empírica: medir o tempo de execução  Dependente de um série de fatores circunstanciais (configuração de máquina, memória, processador, concorrência entre processos, etc.)  Científica (abrangida pela complexidade de algoritmos): expressar o tempo de execução em uma função/expressão matemática  análise formal do desempenho de um algoritmo Introdu ção à Complexidade de Algoritmos Introdução  Relacionando a entrada ao tempo de resposta  O volume de dados manipulados pelos algoritmos chamaremos de entrada.  Existe uma relaç relação direta entre o tempo de resposta e o tamanho da entrada  Muitas vezes desconsideramos o fator má máquina devido a capacidade dos computadores realizarem milhões de cá cálculos por segundo  Mais importante que o fator má máquina é a complexidade do algoritmo  A complexidade de algoritmos define uma expressão matemá matemática entre a entrada (n) e o tempo de resposta (T), definido em funç função do nú número de operaç operações executadas. 2 Introdu ção à Complexidade de Algoritmos Introdução  A notaç notação O  Algoritmos são classificados pelo tipo de expressão matemá matemática que define seu comportamento  Para isso utilizamos o comportamento assintó assintótico da funç função do tempo “T” em relaç relação ao tamanho da entrada do algoritmo “n”  O comportamento assintó assintótico de uma funç função f(x) pode ser encontrado nos casos em que x x∞ (x tende ao infinito) e x-∞, ou seja x varia de ∞ até até - ∞.  Os tipos de comportamento també também são chamados de classes assintó assintóticas  A classe assintó assintótica não define exatamente o nú número de operaç operações, mas qual o comportamento esperado quando variamos o tamanho da entrada.  A classe assintó assintótica é representada pela notaç notação O. Introdu ção à Complexidade de Algoritmos Introdução  Classes assintó assintóticas:  Constante: O(1)  Logarí (n)) (lg Logarítmica: O(lg O(lg(n)) (lg = logaritmo na base 2)  Linear: O(n)  lêlê-se “O de n” n” ou “Ordem de n” n”  LogaritmoLogaritmo-linear: O(n lg(n)) lg(n))  Quadrá Quadrática: O(n2)  Cúbica: O(n3)  Exponencial: O(c O(cn), onde c e uma constante 3 Introdu ção à Complexidade de Algoritmos Introdução Tempo de resposta  Comportamento das classes assintó assintóticas polinomiais n (todas menos O(c O(c ) que é exponencial) Entrada (n) Introdu ção à Complexidade de Algoritmos Introdução Pelos gráficos observamos que:  A classe exponencial tem um comportamento diferente  Todos os algoritmos tendem a ser rápidos quando têm uma entrada pequena, porém quando a entrada aumenta, o tempo de resposta varia drasticamente de uma classe para outra  Um algoritmo O(lg(n)) com uma entrada grande exige um número de operações muito menor que um algoritmo O(c O(cn) com a mesma entrada  Logo se dois algoritmos resolvem o mesmo problema, obviamente preferiremos o algoritmo O(lg(n)) 4 Introdu ção à Complexidade de Algoritmos Introdução  Problemas polinomiais e exponenciais  Ao implementarmos um algoritmo procuramos sempre um que tenha a menor classe assintó assintótica  Problemas que tenham um custo expresso por um polinômio matemá matemático são chamados polinomiais (P).  A única classe que não é polinomial é a exponencial  Algoritmos polinomiais, no geral, são desejados, pois seus tempos normalmente são resolví resolvíveis em tempo computacional (ou seja, executado por um computador em um tempo razoá razoável, de tal forma que o usuá usuário possa aguardar sua resoluç resolução)  Algoritmos não polinomiais não podem ser executados em tempo computacional com uma entrada relativamente grande.  Chamamos de NP (não determiní determinísticos) os problemas que não tenham algoritmos polinomiais descobertos que possam resolvêresolvêlos Introdu ção à Complexidade de Algoritmos Introdução Vejamos na tabela como o tempo de resposta varia de acordo com o aumento de velocidade de processamento e da classe assintótica. Faremos essa análise para entradas 10 e 1.000 5 Introdu ção à Complexidade de Algoritmos Introdução  Vemos que, neste caso, qualquer classe de algoritmo é executá executável em tempo computacional  Na má máquina A, no pior caso, temos 1,024 segundo  Como dito antes, para entradas pequenas, geralmente qualquer classe classe de algoritmo tem um tempo satisfató satisfatório Introdu ção à Complexidade de Algoritmos Introdução  Mesmo na pior classe polinomial O(n3), o aumento da velocidade da má máquina foi bastante relevante.  Máquina A quando a entrada aumentou de 10 para 1.000, o tempo subiu subiu de 1 para 1.000.000 segundos (quase 278 horas)  Na má máquina C, o tempo passou de 0,0001 para 10.000 segundos (menos de 2,78 horas), o que torna a resoluç resolução viá viável e o aumento de velocidade determinante  Já o algoritmo exponencial O(2n), mesmo sendo executado numa má máquina 100 vezes mais rápida, continua intratá intratável com a entrada de tamanho 1.000.  Na má máquina A leva 1,0715E=301 segundos (=3,4449E+287 milhões de anos) já na má máquina C são necessá necessários 3,4449E+282 milhões de anos; ou seja, aumentar a velocidade da má máquina não é uma abordagem útil para se resolver esse tipo de problema.  Já a classe O(lg (n)), é muito boa, pois mesmo aumentando em 100 vezes o tamanho da entrada, O(lg(n)), entrada, o tempo de resposta não aumentou tanto. 6 Introdu ção à Complexidade de Algoritmos Introdução  Regras para aná análise de complexidade de algoritmos  Há duas formas para se determinar o nú número de passos que um algoritmo executa:  A primeira é por meio da contagem direta das operaç operações executadas • Em geral é útil, mas muitas vezes se torna complicada, principalmente quando tratamos de algoritmos recursivos  A segunda é encaixar o nú número de operaç operações a serem executadas em uma funç função de recorrência e depois resolvêresolvêla • Geralmente se aplica a algoritmos recursivos ou mesmo interativos que possam ser transformados em recursivos Introdu ção à Complexidade de Algoritmos Introdução  Apó Após determinar o nú número de passos, podemos definir a ordem do algoritmo (ou sua classe assintó assintótica). Para isso devemos considerar as seguintes regras:  Regra 1: se houver constantes que sejam somadas ou multiplicadas na expressão matemá matemática que define o tempo de resposta de um algoritmo, estas são desconsideradas na definiç definição da classe assintó assintótica do algoritmo  Regra 2: se a expressão que determina o tempo de resposta incluir termos de classes diferentes, vale apenas o maior termo 7 Introdu ção à Complexidade de Algoritmos Introdução  Exemplos: vejamos os algoritmos que têm o tempo dado em função das expressões:  T1 = 2 * n + 10, onde n é a entrada do algoritmo e a classe assintótica é n  T2 = 3n + n2 + 5 * n + 10, onde n é a entrada do algoritmo e a classe assintótica é cn  As classes assintóticas visam identificar o comportamento do algoritmo e não definir seu tempo exato.  Neste contexto, o descarte das constantes faz sentido, pois quando temos entradas muito grandes, as constantes e os termos menores tendem a perder a importância na definição do tempo de resposta. Vejamos o exemplo no slide seguinte... Introdu ção à Complexidade de Algoritmos Introdução Percebe-se que quanto maior a entrada, maior a dependência do valor final com o termo que define a sua classe assintótica. 8 Introdu ção à Complexidade de Algoritmos Introdução  Outras diretivas  Para determinação da classe assintótica, além das duas regras já vistas, consideraremos as seguintes diretivas: Introdu ção à Complexidade de Algoritmos Introdução  Para aná análise de algoritmos, apresentamos alguns somató somatórios que muitas vezes são usados 9 Introdu ção à Complexidade de Algoritmos Introdução  Exemplos de análise de algoritmos, contando operações  Troca de valores  Temos apenas comandos de atribuição e entrada/saída  Todos têm custo constante, O(1) (diretiva a)  A soma dos custos dessas operações seria 5, mas do ponto de vista assintótico, teríamos O(1+1+1+1+1)=O(1), pela regra 1. Introdu ção à Complexidade de Algoritmos Introdução  Média de um vetor •Neste exemplo, temos um laço. Podemos dividir a solução de nosso problema em antes (linha 1), durante e depois do laço (linhas 6,7) •Custo da linha 1 + Custo do laço + custos das linhas 6 e 7 •O(1)+O()+O(max(1,1))= •O(1)+O()+O(1)= •O()= (regra 2) •Continua próximo slide.... 10 Introdu ção à Complexidade de Algoritmos Introdução  Média de um vetor •Precisamos resolver o laço. Nossa entrada refere-se ao tamanho do vetor. Apesar do exemplo definir um vetor de tamanho 100, devemos analisar o algoritmo genericamente, em função de uma entrada de tamanho n. •As linhas 3, 4 e 5 (e ainda as operações embutidas no para) são O(1). Portanto, todo o bloco contido no laço é O(1). Assim: •A complexidade desse algoritmo é expressa pela complexidade do laço. Logo sua complexidade é O(n) Introdu ção à Complexidade de Algoritmos Introdução Soma de matrizes  São dois os laç laços: o interno (linhas 4 a 6) e o externo (linhas 2 a 7).  A linha 1 é O(1)  A complexidade do algoritmo é dada por:  Custo da linha 1 + custo do laç laço externo (linhas 2 a 7) O(1)+O()= O()  Continua pró próximo slide... 11 Introdu ção à Complexidade de Algoritmos Introdução  Nessas transformaç transformações, utilizamos a regra 2  Resolvendo o laç laço interno: Introdu ção à Complexidade de Algoritmos Introdução Podemos resumir o trecho dos dois laços em: Se considerássemos a matriz como quadrada, teríamos  n * n= n2  O(n2) 12 Introdu ção à Complexidade de Algoritmos Introdução Multiplicação de matrizes Para facilitar a compreensão trabalharemos com matrizes quadradas, sendo as duas matrizes da mesma ordem (n x n). Assim, substituiremos as constantes N_LIN1, N_COL1 e N_COL2 por n. Introdu ção à Complexidade de Algoritmos Introdução Multiplicação de matrizes Os laços podem ser representados pelo somatório 13 Introdu ção à Complexidade de Algoritmos Introdução Operações em filas Como o enfileiramento é feito sempre ma última posiç posição, a operaç operação resumeresume-se a um pequeno conjunto fixo de instruç instruções. Portanto o custo é constante O(1). Podemos fazer uma aná análise aná análoga à operaç operação de desenfileiramento, desenfileiramento, que sempre é feita a partir da primeira posiç posição e, portanto, com custo també também constante O(1) Introdu ção à Complexidade de Algoritmos Introdução  Busca sequencial  Nesse problema, não sabemos a priori, quantas vezes o laço será executado.  Dependerá basicamente de um fator: a posição onde o elemento se encontra dentro do vetor (ou mesmo se este existe). Neste caso, devemos aplicar a diretiva b: 14 Introdu ção à Complexidade de Algoritmos Introdução  Esclarecendo...  Pior caso situação em que o algoritmo executa o maior número de operações  Melhor caso menor número de operações  Caso médio é a média das possibilidades  No exemplo anterior:  Melhor caso  elemento procurado estivesse na primeira posição. Neste caso, o custo seria 1  O(1)  Pior caso o elemento não se encontrar no vetor. Nesse caso, todo o vetor seria varrido sem encontrar o elemento. O custo seria, então, n  O(n)  Caso médio precisamos somar o custo de cada possibilidade e dividir pelo número total de possibilidades, pois consideraremos que qualquer evento tem a mesma chance de ocorrer. Não verificamos a possibilidade de o elemento não se encontrar no vetor, pois não temos como determinar qual seria a sua probabilidade. Introdu ção à Complexidade de Algoritmos Introdução  Caso médio...  Os custos são os seguintes: para a primeira posição1, para a segunda2, para a terceira3, e assim sucessivamente, até a última posição em que o custo é n. Assim, teremos:  O número de possibilidades é n. Temos como custo médio: (1+2+3+4+...+n)/n=  e sabemos que  Portanto, temos : n* (n + 1)/2 * 1 / n = (n + 1) / 2  Temos que O(n + 1)/2 = O(n)  Apesar de o caso médio ter a metade do custo do pior caso, continua sendo linear, ou seja, O(n). 15 Introdu ção à Complexidade de Algoritmos Introdução Fatorial  Função de recorrência para resolução do fatorial: n!=n*(n-1)! Para n>0; 0!=1 (base) Introdu ção à Complexidade de Algoritmos Introdução Fatorial  Para o valor 0 (que é a base da função recorrente), o custo é O(1)(custo constante).  Para valores maiores que 0, o algoritmo é executado recursivamente.  O custo é dado pela função recorrente C(n)=C(1)+C(n-1), para n>0.  Podemos notar que a função que determina o custo C é muito semelhante à função recorrente original n!=n*(n-1)!  Portanto: C(0)=c, ou seja, O(1) que é constante C(n)=C(n-1)+c, para n>0 16 Introdu ção à Complexidade de Algoritmos Introdução Fatorial  Resolvendo a recorrência: 17