Projeto e Algoritmos Pontifícia Universidade Católica de Minas Gerais harison@pucpcaldas.br 26 de Maio de 2017
Sumário
A complexidade no desempenho de Quando utilizamos uma máquina boa, ela tende a ter melhor desempenho em de menor complexidade. Classes de complexidade (ordem crescente): 1. Constante: O(1) 2. Logarítmo: O(log n) 3. Linear: O(n) 4. O(nlog n) 5. Quadrático: O(n 2 ) 6. Cúbico: O(n 3 ) 7. Exponencial: O(2 n )
A complexidade no desempenho de Considerando uma máquina X e uma máquina Y que é 10 vezes mais rápida, a relação entre o tamanho máximo de problema resolvível nestas máquinas em relação à complexidade é a seguinte: (baseado em [Toscani e Veloso, 2012]) Complexidade Máquina lenta Máquina rápida log 2 n x 0 (x 0 ) 10 n x 1 10x 1 nlog 2 n x 2 10x 2 n 2 x 3 3, 16x 3 n 3 x 4 2, 15x 4 2 n x 5 x 5 + 3, 3
O estudo de pode levar a compreensão de boas técnicas a serem utilizadas em situações adequadas. Complexidade de Algoritmos de ordenação Técnicas para projeto de
Medida do tempo de Execução de um Programa Conteúdo baseado em [Ziviani, 2003]. 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
Custo de um algoritmo Também é possível analisar em relação à classe do algoritmo: Determinando o menor custo possível para resolver problemas de uma classe, temos a medida da dificuldade inerente para resolver o problema. Quando o custo de um algoritmo é igual ao menor custo possível, o algoritmo é ótimo para a medida de custo considerada.
Exemplo i n t Max( TipoVetor A) { i n t i, Temp ; Temp = A [ 0 ] ; f o r ( i = 1 ; i < N; i ++) i f (Temp < A[ i ] ) Temp = A[ i ] ; r e t u r n Temp ; }
Melhor, Médio e Pior caso Um algoritmo pode ser analisado em relação aos seguintes casos: Melhor Médio Pior Pior caso Maior tempo de execução sobre todas as entradas de tamanho n. Geralmente é a análise utilizada.
Comportamento assintótico de funções A análise de é realizada para valores grandes de n. Fonte: [Ziviani, 2003].
Notações Notação O Dominação superior. Por exemplo, qualquer algoritmo é O(2 n ). Entretanto isto não diz muita coisa, pois procuramos a menor classe de complexidade que domina superiormente. Notação Θ Define a classe do algoritmo em si. Notação Ω Dominação inferior. Quer dizer que um algoritmo não pode ser melhor que esta classe. Por exemplo, os de ordenação são Ω(nlogn), pois não há de ordenação de complexidade linear ou logarítmica.
Operações Algumas das principais operações possíveis nestas notações são: 1. c x O(f(n)) = O(f(n)) com c constante 2. O(f(n)) + O(f(n)) = O(f(n)) 3. O(f(n)) + O(g(n)) = O(max(f(n), g(n))) Suponha três trechos cujos tempos são O(n), O(nlogn) e O(n 2 ). Neste programa então a complexidade é O(max(n, nlogn, n 2 )) = O(n 2 )
Questão do ENADE: Qual o valor de retorno da função a seguir, caso n = 27? i n t r e c ( i n t n ){ i f ( n <= 10){ r e t u r n n 2 ; } e l s e { r e t u r n r e c ( r e c ( n / 3 ) ) ; } } A) 8. B) 9. C) 12. D) 16. E) 18.
Os recursivos, muitas vezes, podem ser facilmente implementados e elegantes. Entretanto o desempenho não é dos melhores. Cálculo dos números de Fibonacci: f 0 = 1 e f 1 = 1 f n = f n 1 + f n 2 u n s i g n e d i n t FibRec ( u n s i g n e d i n t n ) { i f ( n < 2) r e t u r n n ; r e t u r n ( FibRec ( n 1) + FibRec ( n 2 ) ) ; }
O exemplo apresentado é extremamente ineficiente porque recalcula o mesmo valor várias vezes. Versão iterativa possível: u n s i g n e d i n t F i b I t e r ( u n s i g n e d i n t n ){ u n s i g n e d i n t i = 1, k, F = 0 ; f o r ( k = 1 ; k <= n ; k++){ F += i ; i = F i ; } r e t u r n F ; }
Evitar uso de recursividade quando existe solução óbvia por iteração. n 20 30 50 100 Recursiva 1 seg 2 min 21 dias 10 9 anos Iterativa 1/3 ms 1/2 ms 3/4 ms 3/2 ms
Algoritmo de tentativa e erro () Algoritmos tentativa e erro não seguem regra fixa de computação: Passos em direção à solução final são tentados e registrados; Caso esses passos tomados não levem à solução final, eles podem ser retirados e apagados do registro.
: Passeio do cavalo v o i d Tenta ( ) { i n i c i a l i z a s e l e c a o de movimentos ; do{ s e l. proximo c a n d i d a t o ao movimento ; i f ( a c e i t a v e l ){ r e g i s t r a movimento ; i f ( t a b u l e i r o nao e s t a c h e i o ){ t e n t a novo movimento ; i f ( nao s u c e d i d o ){ apaga r e g i s t r o a n t e r i o r ; } } } } w h i l e (! ( movimento bem s u c e d i d o ) ou ( acabaram se os c a n d i d a t o s ) ) ; }
Passeio do Cavalo #d e f i n e N 8 / Tamanho do l a d o do t a b u l e i r o / #d e f i n e FALSE 0 #d e f i n e TRUE 1 i n t i, j, t [N ] [ N], a [N], b [N] ; s h o r t q ; i n t main ( i n t argc, c h a r a r g v [ ] ) { / programa p r i n c i p a l / a [ 0 ] = 2 ; a [ 1 ] = 1 ; a [ 2 ] = 1; a [ 3 ] = 2; b [ 0 ] = 1 ; b [ 1 ] = 2 ; b [ 2 ] = 2 ; b [ 3 ] = 1 ; a [ 4 ] = 2; a [ 5 ] = 1; a [ 6 ] = 1 ; a [ 7 ] = 2 ; b [ 4 ] = 1; b [ 5 ] = 2; b [ 6 ] = 2; b [ 7 ] = 1; f o r ( i = 0 ; i < N; i ++) f o r ( j = 0 ; j < N; j ++) t [ i ] [ j ] = 0 ; t [ 0 ] [ 0 ] = 1 ; / e s c o l h e m o s uma c a s a do t a b u l e i r o / Tenta ( 2, 0, 0, &q ) ; i f (! q ) { p r i n t f ( Sem s o l u c a o \n ) ; r e t u r n 0 ; } f o r ( i = 0 ; i < N; i ++) { f o r ( j = 0 ; j < N; j ++) p r i n t f ( %4d, t [ i ] [ j ] ) ; p u t c h a r ( \ n ) ; } r e t u r n 0 ; }
Passeio do Cavalo v o i d Tenta ( i n t i, i n t x, i n t y, s h o r t q){ i n t u, v ; i n t k = 1; short q1 ; / i n i c i a l i z a s e l e c a o de movimentos / do { ++k ; q1 = FALSE ; u = x + a [ k ] ; v = y + b [ k ] ; i f ( u >= 0 && u < N && v >= 0 && v < N) i f ( t [ u ] [ v ] == 0) { t [ u ] [ v ] = i ; i f ( i < N N) { / t a b u l e i r o nao e s t a c h e i o / / t e n t a novo movimento / Tenta ( i + 1, u, v, &q1 ) / nao s u c e d i d o apaga r e g i s t r o a n t e r i o r / i f (! q1 ) t [ u ] [ v ] = 0 ; } e l s e q1 = TRUE ; } } w h i l e (! ( q1 k == 7 ) ) ; q = q1 ; }
Divisão e conquista Definição Consiste em dividir o problema em partes menores, encontrar soluções para as partes, e combiná-las em uma solução global. Chamada para a metade inicial Chamada para a metade final
dinâmica Quando a divisão de um problema de tamanho n resulta em n subproblemas de tamanho n-1 então é provável que o algoritmo recursivo tenha complexidade exponencial. Nesse caso, a técnica de programação dinâmica pode levar a um algoritmo mais eficiente. dinâmica Calcula a solução para todos os subproblemas, partindo dos subproblemas menores para os maiores, armazenando os resultados em uma tabela.
dinâmica Multiplicação de n matrizes Considere o produto: M = M 1 [10, 20] M 2 [20, 50] M 3 [50, 1] M 4 [1, 100]. A avaliação M = M 1 (M 2 (M 3 M 4 )) requer 125.000 operações, enquanto M = (M 1 (M 2 M 3 )) M 4 requer 2.200 operações.
dinâmica Tentas todas as combinações para os parênteses é exponencial. Com programação dinâmica é possível obter um algoritmo cúbico. calcula os valores de m ij na ordem crescente de i e j. começa com m ii, depois m i,i+1 e assim por diante.
dinâmica m 11 = 0 m 22 = 0 m 33 = 0 m 44 = 0 m 12 = 10.000 m 23 = 1.000 m 34 = 5.000 m 13 = 1.200 m 24 = 3.000 m 14 = 2.200
Algoritmo guloso O nome original (Greedy Algorithm) pode fazer mais sentido como Algoritmo Ganancioso, já que ele busca a melhor solução atual, desconsiderando a visão geral do problema. Exemplo com problema da mochila Exemplo com caixeiro viajante
Notação utilizada nos : Os trabalham sobre os registros de um arquivo. Cada registro possui uma chave utilizada para controlar a ordenação. Podem existir outros componentes em um registro. Considerações iniciais: Um método de ordenação é estável se a ordem relativa dos itens com chaves iguais não se altera durante a ordenação. Alguns dos métodos de ordenação mais eficientes não são estáveis.
A maioria dos métodos de ordenação é baseada em comparações das chaves. Existem métodos de ordenação que utilizam o princípio da distribuição. Exemplo do baralho.
por Seleção Selecione o menor item do vetor. Troque-o com o item da primeira posição do vetor. Repita essas duas operações com os n - 1 itens restantes, depois com os n - 2 itens, até que reste apenas um elemento.
Selection Sort Fonte: DevMedia.
Selection Sort Vantagens: Custo linar para o número de movimentos de registros. É o algoritmo a ser utilizado para arquivos com registros muito grandes. É muito interessante para arquivos pequenos. Desvantagens: O fato de o arquivo já estar ordenado não ajuda em nada, pois o custo continua quadrático. O algoritmo não é estável.
Insertion Sort Em cada passo a partir de i=2 faça: Selecione o i-ésimo item da seqüência fonte. Coloque-o no lugar apropriado na sequência destino de acordo com o critério de ordenação.
Insertion Sort Fonte: DevMedia.
Insertion Sort O número mínimo de comparações e movimentos ocorre quando os itens estão originalmente em ordem. O número máximo ocorre quando os itens estão originalmente na ordem reversa. É o método a ser utilizado quando o arquivo está quase ordenado. É um bom método quando se deseja adicionar uns poucos itens a um arquivo ordenado, pois o custo é linear. O algoritmo de ordenação por inserção é estável.
Shellsort Uma melhoria para o Insertion é evitar a troca entre elementos distantes. Isto é o proposto no Shellsort que precisa de um valor h para tornar a cada passo o registro h-ordenado.
Shellsort Fonte: [Ziviani, 2003].
Shellsort A sequência recomendada para os valores de h são: h(s) = 3h(s 1) + 1, para s > 1 h(s) = 1, para s = 1. h corresponde a 1, 4, 13, 40, 121, 364, 1.093, 3.280...
Shellsort Vantagens: Shellsort é uma ótima opção para arquivos de tamanho moderado. Sua implementação é simples e requer uma quantidade de código pequena. Desvantagens: O tempo de execução do algoritmo é sensível à ordem inicial do arquivo. O método não é estável
Quicksort É o algoritmo de ordenação interna mais rápido que se conhece para uma ampla variedade de situações. A idéia básica é dividir o problema de ordenar um conjunto com n itens em dois problemas menores. Utiliza algoritmo de particionamento: todos elementos à esquerda do pivô são menores que ele e todos à direita são maiores. Os problemas menores são ordenados independentemente. Os resultados são combinados para produzir a solução final.
Quicksort Vantagens: É extremamente eficiente para ordenar arquivos de dados. Necessita de apenas uma pequena pilha como memória auxiliar. Requer cerca de n log n comparações em média para ordenar n itens. Desvantagens: Tem um pior caso O(n 2 ) comparações. Sua implementação é muito delicada e difícil O método não é estável.
Heapsort Algoritmo baseado no uso de filas de prioridades. Custo de construção linear - O(n). Custo de inserção, retirada, substituição e alteração logarítmico - O(log n).
Heap É uma sequência de itens com chaves c[1], c[2],..., c[n], tal que: c[i] >= c[2i], c[i] >= c[2i + 1] ou: [9, 7, 6, 4, 3, 1]
Problemas P e NP Problemas polinomiais Problemas exponenciais Propriedade Um problema da classe NP poderá ser resolvido em tempo polinomial se e somente se todos os outros problemas em NP também puderem.
Problemas P, NP Fonte: [Ziviani, 2003] Fácil: Existe um caminho de i até j com peso <= k? Há um algoritmo eficiente com complexidade de tempo O(A log V ), sendo A o número de arestas e V o número de vértices (algoritmo de Dijkstra). Difícil: Existe um caminho de i até j com peso >= k? Não existe algoritmo eficiente. É equivalente ao PCV em termos de complexidade.
Coloração de um grafo Definição Otimização de compiladores Artigo na Science
Ciclo de Hamilton É um caso especial do problema do caixeiro viajante. Fonte: [Ziviani, 2003] Exemplo de ciclo de Hamilton: 0 1 4 2 3 0. Exemplo de caminho de Hamilton: 0 1 4 2 3.
Cobertura de arestas e de vértices V = {3, 4, 5}.
Algoritmos deterministas Algoritmo determinista O resultado de cada operação é definido de forma única. Algoritmo não-determinista Capaz de escolher uma dentre as várias alternativas possíveis a cada passo
Pesquisa não-determinista
Problema da Satisfabilidade (SAT) Definição Complexidade exponencial para encontrar todas combinações
Caracterização das classes P e NP P Conjunto de todos os problemas que podem ser resolvidos por deterministas em tempo polinomial. NP Conjunto de todos os problemas que podem ser resolvidos por não-deterministas em tempo polinomial. Para mostrar que um determinado problema está em NP, basta apresentar um algoritmo não-determinista que execute em tempo polinomial para resolver o problema. Outra maneira é encontrar um algoritmo determinista polinomial para verificar que uma dada solução é válida.
Diferenças entre P e NP Não há definição. Consequências de P = NP e de P!= NP.
Transformação polinomial
Conjunto Independente de Vértices de um Grafo
Clique de um grafo
Problema NP-completo e NP-difícil
Teorema de Cook Satisfabilidade (SAT) está em P se e somente se P = NP. Ou seja, se existisse um algoritmo polinomial determinista para satisfabilidade, então todos os problemas em N P poderiam ser resolvidos em tempo polinomial.
Problemas NP Possível solução com exponenciais eficientes como o Simplex. Usar heurísticas.
[[Toscani e Veloso, 2012] ]Laira V. Toscani e Paulo A. S. Veloso (2012) Complexidade de. 3 edição. Série de livros didáticos informática UFRGS [[Ziviani, 2003] ]Nivio Ziviani (2003) Projeto de Algoritmos com implementações em Pascal e C Cengage Learning