7. Introdução à Complexidade de Algoritmos Fernando Silva DCC-FCUP Estruturas de Dados Fernando Silva (DCC-FCUP) 7. Introdução à Complexidade de Algoritmos Estruturas de Dados 1 / 1 Análise de Algoritmos No desenvolvimento de algoritmos é importante ter a noção da eficiência de um algoritmo, i.e. da eficiência que pode ter uma implementação do algoritmo. Eficiência normalmente mede-se em tempo de execução ou espaço (memória) necessário à execução do algoritmo (ou programa associado). Tempo de Execução de um algoritmo varia com o input e normalmente aumenta com o tamanho do input. caso médio é difícil de determinar. normalmente, olha-se para o pior caso possível de tempo de execução: mais simples de analizar crucial nas aplicações mais exigentes, jogos, etc. Fernando Silva (DCC-FCUP) 7. Introdução à Complexidade de Algoritmos Estruturas de Dados 2 / 1
Como aferir a eficiência Como aferir a eficiência, por exemplo, o tempo de execução? Análise empírica executando o programa com inputs de tamanho e composição variados, mas nem sempre é simples concretizar o algoritmo, ou os resultados podem não ser conclusivos, comparação de algoritmos obriga a usar igual software e hardware. Análise teórica usa uma descrição de mais alto nível do algoritmo em vez da implementação, caracteriza o tempo de execução como uma função do tamanho do input, n, tem em conta todos os possíveis inputs, permite avaliar eficiência de forma independente do ambiente de hardware/software. Fernando Silva (DCC-FCUP) 7. Introdução à Complexidade de Algoritmos Estruturas de Dados 3 / 1 Exemplo de análise de complexidade - findmax() Como calcular o tempo de execução do algoritmo seguinte: Pressupostos (RAM -Random Access Memory): memória ilimitada e endereços contêm um n o arbitrário ou caracteres, aceder ao conteúdo de um endereço custa uma unidade de tempo. max= A[0]; 1 leitura de A[0] + 1 atribuição a max. Fernando Silva (DCC-FCUP) 7. Introdução à Complexidade de Algoritmos Estruturas de Dados 4 / 1
Determinar complexidade do findmax() Casos possíveis: caso mais favorável (A[0] é o maior elemento): t(n) = 2 + 1 + n + 4(n 1) + 1 = 5n operações primitivas pior caso: t(n) = 2 + 1 + n + 6(n 1) + 1 = 7n 2 caso médio? difícil de calcular, depende da distribuição do input; usar teoria de probabilidades. Normalmente, olharemos para o pior caso, pois dá-nos um limite superior do tempo de execução. Cálculo do tempo de execução: seja a o tempo observado para a operação primitiva mais rápida seja b o tempo observado para a operação primitiva mais lenta então, o pior tempo de execução T (n) será a(7n 2) T (n) b(7n 2) T (n) limitado por 2 funções lineares Fernando Silva (DCC-FCUP) 7. Introdução à Complexidade de Algoritmos Estruturas de Dados 5 / 1 Taxa de crescimento do tempo de execução Saliente-se que: o tempo de execução, T (n), pode ser afectado pela alteração do ambiente hardware/software, mas tal não acontece se considerarmos a taxa de crescimento de T (n) A taxa de crescimento de T (n) corresponde ao crescimento de T (n) quando se aumenta o valor de n. O exemplo findmax() mostrava T (n) limitado por 2 funções lineares em n, significando que o tempo de execução varia na mesma proporção que n. Logo, diz-se que o crescimento de T (n) é linear. Fernando Silva (DCC-FCUP) 7. Introdução à Complexidade de Algoritmos Estruturas de Dados 6 / 1
Taxas de crescimento (1) logarítmica log 2 n linear n quadrática n 2 cúbica n 3 polinomial n k exponencial a n (a > 1) Crescimento de algumas funções: n log 2 n n n n log2 n n 2 n 3 2 n 2 1 1.4 2 2 4 8 4 8 3 2.8 8 24 64 512 256 16 4 4.0 16 64 256 4096 65536........................ 1024 10 32 1024 10240 > 10 6 > 10 9 > 10 308 Fernando Silva (DCC-FCUP) 7. Introdução à Complexidade de Algoritmos Estruturas de Dados 7 / 1 Taxas de crescimento (2) Se assumirmos que cada operação pode ser executada em 1µs (micro-sec), qual será o maior problema (função de n) para um programa que execute em 1 seg., 1 min., 1 hora? Tamanho máximo problema (n) T (n) 1 seg. 1 min. 1 hora 400n 2500 150,000 9,000,000 20n log n 4096 166,666 7,826,087 2n 2 707 5,477 42,426 n 4 31 88 244 2 n 19 25 31 No caso de crescimento exponencial, apenas conseguimos tratar problemas para dimensões muito pequenas de n. Fernando Silva (DCC-FCUP) 7. Introdução à Complexidade de Algoritmos Estruturas de Dados 8 / 1
A definição big-oh O() Definição de ordem de complexidade assimptótica: Sejam f (n) e g(n) funções de N 0 R. f (n) é O(g(n)) (ou seja, f (n) é da ordem de g(n)) se c > 0, n 0 1 : f (n) cg(n), n n 0 Interpretação gráfica: O(g(n)) corresponde ao limite superior da taxa de crescimento de f (n). dizer que f (n) é O(g(n)), significa dizer que f (n) não cresce mais do que g(n). Fernando Silva (DCC-FCUP) 7. Introdução à Complexidade de Algoritmos Estruturas de Dados 9 / 1 Exemplos - O() 2n + 10 é O(n) porque: 2n + 10 cn (c 2)n 10 n 10 (c 2) para c = 3 e n 0 = 10 verifica-se sempre que 2n + 10 cn outras funções: 20n 3 + 10n log n + 5 O(n 3 ) a k n k + a k 1 n k 1 +... + a 0 O(n k ) 3 log n + log log n O(log n) 2 100 O(1) 5 n O( 1 n ) Fernando Silva (DCC-FCUP) 7. Introdução à Complexidade de Algoritmos Estruturas de Dados 10 / 1
Regras de simplificação Algumas regras que olhando para expressões mais complexas, permitem-nos fazer simplificações: Se d(n) O(f (n)), então ad(n) (com a > 0) O(f (n)) Se d(n) O(f (n)) e e(n) O(g(n)), então d(n) + e(n) O(f (n) + g(n)) Se d(n) O(f (n)) e e(n) O(g(n)), então d(n)e(n) O(f (n)g(n)) Se d(n) O(f (n)) e f (n) O(g(n)), então d(n) O(g(n)) Fernando Silva (DCC-FCUP) 7. Introdução à Complexidade de Algoritmos Estruturas de Dados 11 / 1 Complexidade do método da bolha (bubble-sort) Façamos a análise de complexidade do método da bolha: Efectivamente, o número de iterações total é a soma das iterações que o ciclo j faz para cada valor de i, i.e. n 1 i=1 (n i) = (n 1) + (n 2) +... + 2 + 1 = = n(n 1) 2 = n2 2 n 2 O(n2 ) Fernando Silva (DCC-FCUP) 7. Introdução à Complexidade de Algoritmos Estruturas de Dados 12 / 1
Complexidade de funções recursivas Considere-se a função factorial fact() e seja T (n) o tempo de execução para fact(n), então: T (n) = c + T (n 1), se n > 1 d, se n 1 assumindo n 2: T (n 1) = c + T (n 2) T (n) = 2c + T (n 2) em geral: T (n) = ic + T (n i), n > i se i = n 1, então T (n) = c(n 1) + T (1) = c(n 1) + d donde podemos concluir T (n) O(n), i.e. fact() tem complexidade n. Fernando Silva (DCC-FCUP) 7. Introdução à Complexidade de Algoritmos Estruturas de Dados 13 / 1 Complexidade da pesquisa binária (1) Considere-se o método pesqbin() (pesquisa binária não recursiva): Inicialmente o intervalo de valores é n (e = 0 e d = n 1). Em cada passo do ciclo, o intervalo reduz-se a metade. Portanto, a questão que se coloca é: dado um inteiro n, quantas divisões inteiras por 2 são necessárias para que chegue a 1? i.e. qual dos valores seguintes será o primeiro a ser < 1? n/2, n/4, n/8,..., n/2 k,... Fernando Temos Silva (DCC-FCUP) de resolver n/2 7. Introdução k < 1, à Complexidadeo que dádek Algoritmos > log 2 n Estruturas de Dados 14 / 1
Complexidade da pesquisa binária (2) É necessário resolver a equação: n/2 k < 1 se aplicarmos logaritmos, temos k > log 2 n Podemos então dizer que: com k = log2 n sabemos que ao fim de um máximo de k iterações, encontramos o valor ou podemos concluir que o valor que procuramos não existe. Em resumo, dizemos que a função pesqbin() tem complexidade logarítmica, O(log 2 n), pois o número de iterações necessárias não excede log 2 n, sendo n a dimensão dos dados. Fernando Silva (DCC-FCUP) 7. Introdução à Complexidade de Algoritmos Estruturas de Dados 15 / 1 Complexidade de árvores binárias de pesquisa (1) Ver primeiro capítulo sobre árvores! Se inserirmos n valores aleatórios numa árvore binária de pesquisa, sabemos que o comprimento médio do caminho da raíz até uma folha é O(log2 n) correspondente à profundidade da árvore. verificar se x pertence à árvore é assim O(log2 n) Se a árvore for completa, então nenhum caminho da raíz até um dado nó pode ter mais do que 1 + log 2 n nós. métodos insertbtnode(), contains() e removebtnode() são O(log 2 n). É comum que ao inserirmos n valores aleatórios, a árvore não seja completa, pode até ficar uma sequência ordenada. Neste caso cada inserção é O(n). Fernando Silva (DCC-FCUP) 7. Introdução à Complexidade de Algoritmos Estruturas de Dados 16 / 1
Complexidade de árvores binárias de pesquisa (2) A questão a saber é se uma árvore binária em média estará próxima de uma árvore completa ou de uma sequência? isto decide se o tempo médio é O(log 2 n) ou O(n). A demonstração deste resultado é complexa, envolvendo probabilidades, basta-nos aqui saber que é possível mostrar por indução que a complexidade será O(log 2 n). Fernando Silva (DCC-FCUP) 7. Introdução à Complexidade de Algoritmos Estruturas de Dados 17 / 1