Teoria da Computação Aula 8 Noções de Complexidade Prof. Esp. Pedro Luís Antonelli Anhanguera Educacional
Motivação: Por que estudar algoritmos? Perguntas: - Por que estudar algoritmos se os computadores são cada vez mais rápidos! Qual a necessidade de estudar problemas e algoritmos eficientes que os solucionam?
Motivação: Por que estudar algoritmos? Por que estudar algoritmos se os computadores são cada vez mais rápidos! Resp.: Os computadores são rápidos, mas não infinitamente rápidos!
Motivação: Por que estudar algoritmos? Qual a necessidade de estudar problemas e algoritmos eficientes que os solucionam? Resp. : Se o desempenho computacional cresce, o tamanho do problema também. - Big Data; - I. A.; - Logística; - Redes de Computadores; - E-commerce (criptografia); - Sequenciamento de DNA; - Internet das Coisas (IoT).
Exemplo - Ordenação Suponha que os computadores A e B executam 1G e 10M operações por segundo, respectivamente. ( observe que A é 100 vezes mais rápido que B) Algoritmo 1: Ordenação por Inserção: Algoritmo 2: Ordenação por Intercalação:
Exemplo - Ordenação Algoritmo 1: Ordenação por Inserção, implementado na máquina A por um excelente programador, utilizando-se da linguagem de máquina (ultra-rápida). - Função de Custo de tempo- T(n) : 2 n² instruções; Algoritmo 2: Ordenação por Intercalação, implementado na máquina B por um programador mediano, utilizando-se de uma linguagem de alto nível e compilador convencional. - Função de Custo de tempo- T(n) : 50 n lg n instruções; Considerando que c1=2 e c2=50 são determinadas por detalhes de implementação, como linguagem, habilidades do programador, etc. Estamos considerando que isso faz um programa 25x mais rápido.
Exemplo - Ordenação O que acontece quando tentamos ordenar 1 milhão de elementos? Qual o computador executa essa tarefa em menos tempo?
Exemplo - Ordenação Analisando os resultados de maneira simplista: Mesmo que o computador A seja 100x mais rápido que B, e a implementação seja 25x melhor, considerando o algoritmo executado e a instância (tamanho) do problema, B foi mais eficiente! A máquina B resolveu o problema 20 vezes mais rápido que a máquina A. Se o vetor possuir 10 milhões de elementos, a razão será de 2,3 dias para 20minutos.
Exemplo Matriz Problema: Calcular o produto de duas matrizes ( C = A * B) de dimensão N x N.
Exemplo Matriz Problema: Calcular o produto de duas matrizes ( C = A * B) de dimensão N x N. Versão 1: int A [N][N], B [N][N], C [N][N]; int i, j, k; for (i=0; i<n; i++) for (j=0; j<n; j++) for (k=0; k<n; k++) C[i][j] += A[i][k]*B[k][j];
Exemplo Matriz Problema: Calcular o produto de duas matrizes ( C = A * B) de dimensão N x N. Versão 2: int A [N][N], B [N][N], C [N][N], T [N][N], ; int i, j, k; T = transposta(b); for (i=0; i<n; i++) for (j=0; j<n; j++) for (k=0; k<n; k++) C[i][j] += A[i][k]*T[j][k]; Obs. Mesma operação, porém a transposta de B é calculada (resultado em T) e usada ( linhas e colunas são invertidas ).
Exemplo Matriz Qual versão ( versão 1 ou 2) tem melhor desempenho (executa mais rapidamente)? Nos processadores mais antigos a versão 1 é mais rápida. Nos processadores atuais a versão 2 é mais rápida ( para N=1024, cerca de 5x em um Intel i5). Porque?
Análise de Algoritmos Sugestão: Podemos considerar apenas o seu tempo de execução? O tempo de Execução não depende apenas do algoritmo, mas também: - Arquitetura e Conjunto de Instruções da máquina que executa; - Qualidade do Compilador; - Habilidade do Programador;
Análise de Algoritmos Situação comum: Para um determinado problema (relação entre entrada e saída) há vários algoritmos (processo de solução) e deseja-se escolher um deles. Como comparar algoritmos diferentes? Resposta intuitiva: extraindo uma medida comum de eficiência entre eles. Sugestão: Podemos considerar seu tempo de execução?
Análise de um Algoritmo em particular Qual é o custo de usar um dado algoritmo para resolver um problema específico? Características que devem ser investigadas: -análise do número de vezes que cada parte do algoritmo deve ser executada; - estudo da quantidade de memória necessária; - estudo do tempo em várias situações.
Análise de uma classe de Algoritmos Qual é o algoritmo de menor custo possível para resolver um problema em particular? Toda uma família de algoritmos é investigada. Procura-se identificar um que seja o melhor possível. Coloca-se limites para a complexidade computacional dos algoritmos pertencentes à classe
Custo de um Algoritmo Determinando o menor custo possível para resolver problemas de uma dada classe, temos a medida da dificuldade inerente para resolver o problema. Podem existir vários algoritmos para resolver o mesmo problema, como no caso da ordenação de um conjunto. - Alguns algoritmos de ordenação: -Selection Sort; -Insertion Sort; - Buble Sort; - Shell Sort; - Merge Sort; - Quick Sort; - Heap Sort; - Radix Sort; - Counting Sort; - Bucket Sort.
Custo de um Algoritmo Atenção! Algoritmo Eficaz Algoritmo Eficiente Algoritmo Ótimo Algoritmo Eficaz: É aquele resolve o problema em qualquer de suas instâncias; Algoritmo Eficiente: É aquele resolve o problema em qualquer de suas instâncias de maneira satisfatória; Algoritmo Ótimo: É aquele resolve o problema em qualquer de suas instâncias com o menor custo possível. Se a mesma medida de custo é aplicada a diferentes algoritmos, então é possível compará-los e escolher o mais adequado.
Análise de Algoritmos Não há como comparar algoritmos, utilizando máquinas/tecnologias diferentes. Ao invés de escolher uma máquina em particular: A ideia é analisar o algoritmo utilizando um modelo matemático de computação. Modelo utilizado: Máquina de Acesso Aleatório (Random Acess Machine- RAM ); Único processador, com instruções executadas uma após a outro, sem concorrência; Primitivas/Operações Básicas: Soma, subtração, multiplicação, divisão, movimentação de dados, desvios, etc; A execução de qualquer primitiva leva uma unidade de tempo.
Função de Complexidade Para medir o custo de execução de um algoritmo é comum definir uma função de custo ou função de complexidade f. Função de complexidade de tempo: f(n) mede o tempo necessário para executar um algoritmo em um problema de tamanho n. Observação: A complexidade de tempo na realidade não representa tempo diretamente, mas o número de vezes que determinada operação considerada relevante é executada. Função de complexidade de espaço: f(n) mede a memória necessária para executar um algoritmo em um problema de tamanho n.
Comportamento Assintótico de Funções O parâmetro n fornece uma medida da dificuldade para se resolver o problema. Para valores suficientemente pequenos de n, qualquer algoritmo custa pouco para ser executado, mesmo os ineficientes, então a escolha do algoritmo não é um problema crítico para problemas de tamanho pequeno. Logo, a análise de algoritmos é realizada para valores grandes de n. Estuda-se o comportamento assintótico das funções de custo (comportamento de suas funções de custo para valores grandes de n). O comportamento assintótico de f(n) representa o limite do comportamento do custo quando n cresce.
Principais Classes de Funções de Custo Classe do tipo f(n) = C(1). Algoritmos de complexidade são ditos de complexidade constante. Uso do algoritmo independe de n. As instruções do algoritmo são executadas um número fixo de vezes.
Principais Classes de Funções de Custo Classe do tipo f(n) = log n Um algoritmo de complexidade log n é dito ter complexidade logarítmica. Típico em algoritmos que transformam um problema em outros menores. Pode-se considerar o tempo de execução como menor que uma constante muito grande. Quando n é mil, log 2 n = 10, quando n é 1 milhão, log 2 n = 20. Para dobrar o valor de log n temos de considerar o quadrado de n. A base do logaritmo muda pouco estes valores: quando n é 1 milhão, o log 2 n é 20 e o log 10 n é 6.
Principais Classes de Funções de Custo Classe do tipo f(n) = n Um algoritmo de complexidade n é dito ter complexidade linear. Em geral, um pequeno trabalho é realizado sobre cada elemento de entrada. É a melhor situação possível para um algoritmo que tem de processar/produzir n elementos de entrada/saída. Cada vez que n dobra de tamanho, o tempo de execução dobra.
Principais Classes de Funções de Custo Classe do tipo f(n) = n. log n Típico em algoritmos que quebram um problema em outros menores, resolvem cada um deles independentemente e ajuntando as soluções depois. Quando n é 1 milhão, n.log 2 n é cerca de 20 milhões. Quando n é 2 milhões, n.log 2 n é cerca de 42 milhões, pouco mais do que o dobro.
Principais Classes de Funções de Custo Classe do tipo f(n) = n 2 Um algoritmo de complexidade n 2 é dito ter complexidade quadrática. Ocorrem quando os itens de dados são processados aos pares, muitas vezes em um laço dentro de outro. Quando n é mil, o número de operações é da ordem de 1 milhão. Sempre que n dobra, o tempo de execução é multiplicado por 4. Úteis para resolver problemas de tamanhos relativamente pequenos.
Principais Classes de Funções de Custo Classe do tipo f(n) = n 3 Um algoritmo de complexidade n 3 é dito ter complexidade cúbica. Úteis apenas para resolver pequenos problemas. Quando n é 100, o número de operações é da ordem de 1 milhão. Sempre que n dobra, o tempo de execução fica multiplicado por 8.
Principais Classes de Funções de Custo Classe do tipo f(n) = 2 n Um algoritmo de complexidade 2 n é dito ter complexidade exponencial. Geralmente não são úteis sob o ponto de vista prático. Ocorrem na solução de problemas quando se usa força bruta para resolvê-los. Quando n é 20, o tempo de execução é cerca de 1 milhão. Quando n dobra, o tempo fica elevado ao quadrado.
Principais Classes de Funções de Custo Classe do tipo f(n) = n! Um algoritmo de complexidade n! é dito ter complexidade exponencial, apesar de O(n!) ter comportamento muito pior do que 2 n. Geralmente ocorrem quando se usa força bruta na solução do problema. Quando n = 20,temos 20! = 2432902008176640000, um número com 19 dígitos. Quando n = 40, temos 40!, que é um número com 48 dígitos.
Principais Classes de Funções de Custo O ideal seria que as operações de estruturas de dados executassem com tempos de execução proporcional as funções constante ou logarítmica. Seria desejável que os algoritmos executassem em tempo linear ou n.log(n). Algoritmos com tempos de execução quadráticos ou cúbicos são pouco práticos. Algoritmos com tempos de execução exponenciais são impraticáveis a não ser para pequenas entradas. Escala de Complexidade Melhor -------------------------------------------------------------------------------- Pior
Principais Classes de Funções de Custo
Principais Classes de Funções de Custo
Principais Classes de Funções de Custo
Bibliografia da apresentação: ZIVIANI, N. Projeto de Algoritmos com implementações em Pascal e C. 2ª edição. São Paulo: Pioneira Thomson Learning, 2005. 552 p. Disponível em http://www2.dcc.ufmg.br/livros/algoritmos/ Goodrich, Michael e Tamassia, Roberto - Estrura de dados e algorítmos em Java 4 ed. Porto Alegre - Bookrnan. 2007. http://www.inf.ufrgs.br/~prestes/courses/complexity/aula1.pdf
Bibliografia básica da disciplina SIPSER, Michael. INTRODUÇÃO À TEORIA DA COMPUTAÇÃO. 1ª ed. São Paulo: Pioneira - Thomson Learning, 2007. LEWIS, Harry R; PAPADIMITRIOU, Christos H. Elementos de Teoria da Computação. 2ª ed. Porto Alegre: Bookman, 2000. DIVERIO, TIARAJU ASMUZ; MENEZES, Paulo Fernando Blauth. Teoria da Computação : maquinas universais e computabilidade. 2ª ed. Porto Alegre: Sagra Luzzatto, 2000.
Bibliografia complementar da disciplina MENEZES, Paulo Fernando Blauth. Linguagens Formais e Autômatos. 5ª ed. Porto Alegre: Bookman, 2008. HOPCROFT, John E; ULLMAN, Jeffrey D; MOTWANI, Rajeev, SOUZA. Introdução a Teoria dos Autômatos, Linguagens e Computação. 1ª ed. São Paulo: Campus - Elsevier, 2003. HAEUSLER, EDWARD HERMANN; MENEZES, PAULO BLAUTH. TEORIA DAS CATEGORIAS : PARA CIÊNCIA DA COMPUTAÇÃO. 2ª ed. Porto Alegre: Sagra Luzzatto, 2001 by DSC/UFCG