Introdução à Complexidade de Algoritmos Área que visa determinar a complexidade (custo) de um algoritmo, com isso é possível: y Comparar algoritmos: existem algoritmos que resolvem o mesmo tipo de problema. y Determinar D t i se o algoritmo l it é “óti “ótimo”: ” os algoritmos l it apresentam um limite inferior para a sua complexidade (definido matematicamente). |Se a complexidade é ótima então está abaixo deste limite. Fonte: UFPB (http://www.slideshare.net/alexandrend/anlise-de-complexidade-de-algoritmos) Introdução à Complexidade de Algoritmos Um algoritmo serve para resolver um determinado problema, e todos os problemas têm sempre uma entrada de dados | O tamanho dessa entrada (N) tem geralmente efeito direto no tempo de resposta de um algoritmo | Dependendo do problema a ser resolvido resolvido, já existem algoritmos prontos ou que podem ser adaptados | | O problema é: qual algoritmo escolher? Introdução à Complexidade de Algoritmos | Pode-se falar de dois tipos de complexidade de algoritmos : Complexidade Espacial: Quantidade de recursos utilizados p para resolver o p problema;; y Complexidade Temporal: Quantidade de tempo utilizado. y | Pode ser vista também como o número de instruções necessárias para resolver um determinado problema; Em ambos os casos, a complexidade é medida de acordo com o tamanho dos dados de entrada (n) | Estamos mais interessados em calcular a Complexidade Temporal de um algoritmo! | Introdução à Complexidade de Algoritmos | Existem três perspectivas para análise de complexidade: Melhor Caso (Ω) y Caso Médio (θ) y Pior Caso (O) y | Nas três perspectivas perspectivas, a função f(n) retorna a complexidade de um algoritmo com entrada de tamanho n Introdução à Complexidade de Algoritmos ANÁLISE DO MELHOR CASO Definido pela letra grega Ω(Ômega) | Exprime o menor tempo de execução de um algoritmo g p para uma entrada de tamanho n | É pouco usado, por ter aplicação em poucos casos. casos | Ex.: | O algoritmo de pesquisa sequêncial em um vetor tem complexidade f(n) = Ω(1) y A análise assume que o número procurado seria o primeiro selecionado na lista. y | Abordagem otimista! Introdução à Complexidade de Algoritmos ANÁLISE DO CASO MÉDIO Definido p pela letra g grega g θ ((Theta)) | Deve-se obter a média dos tempos de execução de todas as entradas de tamanho n n, ou baseado em probabilidade de determinada condição ocorrer | Ex.: Ex : | O algoritmo de pesquisa sequêncial em um vetor tem complexidade f(n) = θ(n/2) y Em média será necessário visitar n/2 elementos do vetor até encontrar o elemento procurado p y Melhor aproximação | Muito difícil de determinar na maioria dos casos | Introdução à Complexidade de Algoritmos ANÁLISE DO PIOR CASO | Representado pela letra grega O (big O) y Big O maiúsculo. Trata-se da letra grega ômicron maiúscula Baseia-se no maior tempo de execução sobre todas as entradas de tamanho n (ordem de grandeza) | É o método mais fácil de se obter ((o mais usado). ) | Ex.: | O algoritmo de pesquisa sequêncial em um vetor tem complexidade f(n) = O(n) y No p pior caso será necessário visitar todos os n elementos do vetor até encontrar o elemento procurado y | Abordagem pessimista! Introdução à Complexidade de Algoritmos A Notação ç O | Tempo (ou espaço) é contabilizado em número de passos do algoritmo (unidade de armazenamento) | Análise do algoritmo determina uma função que depende do tamanho da entrada n n. | à medida que n aumenta, aumenta a complexidade do algoritmo (esforço computacional) 8 Introdução à Complexidade de Algoritmos A Notação ç O | Desprezar constantes aditivas ou multiplicativas y | Número de passos 3n será aproximado para n Interesse assintótico y termos de menor grau podem ser desprezados n2 + n será aproximado para n2 | 6n3 + 4n - 9 será aproximado para n3 | 9 Introdução à Complexidade de Algoritmos Cálculo da complexidade p do algoritmo g | Foi visto que, para calcular a complexidade de um algoritmo, deve-se deve se analisar o pior caso (O(x)) | A análise deve ser feita de acordo com a tabela a seguir Nú Número de d Operações O õ C Complexidade l id d f(n) O(f(n)) c x f(n) O(f(n)) f(n) + f(n) O(f(n)) f(n) + g(n) O(max(f(n),g(n)) f(n) x g(n) O(f(n) x g(n)) Introdução à Complexidade de Algoritmos Técnicas de análise da complexidade do algoritmo Alguns princípios podem ser seguidos: 1. O tempo de execução de um comando de atribuição, de leitura ou de escrita pode ser considerado como O(1). 2. O tempo de execução de um comando de decisão é O(1); 3 O tempo para executar um looping é a soma do tempo de 3. execução do corpo do looping, multiplicado pelo número de iterações do looping looping. 4. Quando o programa possui funções não recursivas, o tempo de execução de cada função deve ser computado separadamente um a um, iniciando com funções que não chamam outras funções funções. | Introdução à Complexidade de Algoritmos Cálculo da complexidade p do algoritmo g – Ex1 f(x)=1 f(x) 1+1+1+n*(n*(n*(1+1+1+3 ))) 3+n*n* n*6 3 + 6n3 f(x) = O(n3) Desprezouse os termos de menor grau ORDENS DE ALGORITMOS Complexidade Constante | Complexidade Linear | Complexidade Logarítmica | Complexidade Log Linear | Complexidade Quadrática | Complexidade Cúbica | Complexidade Exponencial | Complexidade Fatorial | COMPLEXIDADE CONSTANTE - O(1) São os algoritmos onde a complexidade independe do tamanho n de entradas | É o único em que as instruções dos algoritmos são executadas um número fixo de vezes | if (condição == true) then { realiza alguma operação em tempo constante 1 } ) else { 1+( g operação p ç em tempo p constante realiza alguma } | 1+( f( ) = 1 f(x) 1+(1)*1+(1) (1)*1 (1) = 4 => O(1) 1 ) COMPLEXIDADE LINEAR – O(N) | Uma operação é realizada em cada elemento de entrada, ex.: pesquisa de elementos em uma lista for (i = 0; i < N; i++) { if (condição == true){ comandos de leitura, escrita ou condicionais; 1 ) else { 1+( } | 1+( } comandos de leitura, escrita ou condicionais; } n*( 1 ) ) f(x) = n*(1+(1)*1+(1))=> n*(2*2) = n(4) =>O(n) COMPLEXIDADE LOGARÍTMICA O(LOG2N) | Ocorre tipicamente em algoritmos que dividem o problema em problemas menores int PesquisaBinaria ( int array[], int chave , int N){ int inf = 0; int sup = N - 1; int meio; while (inf <= sup) { meio = (inf+sup)/2; if (chave == array[meio]){ return meio; } else{ if (chave < array[meio]){ sup = meio-1; } else{ inf = meio+1; } } } return -1; // não encontrado } | ( 1+ 1+ 1 n*( 1+ 1+( 1 ) 1+( 1+( 1 ) 1+( 1 ) ) ) 1 ) f(x) = (1+1+1*n*(1+1+(1)*1+(1+(1)*1+(1)))*1) = 3*n(6). Como Log2(6) = 3 => (Log2(n)) COMPLEXIDADE LOG LINEAR – O(NLOG2N) | Ocorre tipicamente em algoritmos que dividem o problema em problemas menores, porém juntando posteriormente a solução dos problemas menores. void merge(int inicio, int fim) { if (inicio < fim) { int meio = (inicio + fim) / 2; merge(inicio meio); merge(inicio, merge(meio + 1, fim); mesclar(inicio, meio, fim); } } COMPLEXIDADE QUADRÁTICA – O(N²) | Itens são processados aos pares, geralmente com um loop dentro do outro void bubbleSort(int[] a) { for (int i = 0; i < a.length-1; i++) { for (int j = 0; j < a.length a length-1; 1; jj++)) { if (a[j] > a[j+1]) { swap(a, j, j+1); } } } } | ( n*( n*(( n 1+( 3 ) ) ) ) f(x) = (n*(n*(1+(3)))) = (n*(n+4)) = n2+4n => O(n2) COMPLEXIDADE CÚBICA – O(N³) | | Itens são processados três a três, geralmente com um loop dentro do outros dois int dist[N][N]; int i, j, k; 1 1+ 1+ for ( k = 0; k < N; k++ ) f ( i = 0; i < N; i++ ) for for ( j = 0; j < N; j++ ) dist[i][j] = min( dist[i][j], dist[i][k] + dist[k][j] ); n*( n*( *( n*( 1))) f(x) = 1+1+n*(n*(n*(1))) = 4+n3 => O(n3) COMPLEXIDADE EXPONENCIAL – O(2N) Utilização de “Força Bruta” para encontrar a solução de um problema. | A solução geralmente é baseada diretamente no enunciado do problema e nas definições dos conceitos envolvidos | Ex.: Ex : | Utilizando apenas números é possível criar 10n senhas de n dígitos y Um algoritmo de força bruta para quebrar uma dessas senhas tem complexidade O(2n) y COMPLEXIDADE FATORIAL – O(N!) Também é baseada na utilização de força bruta para encontrar a solução de um problema | Consiste em testar todas as possíveis permutações existentes na solução à procura da solução ótima para o problema | Ex.: Ex : Problema do Caixeiro Viajante | Encontrar a rota mínima para visitar várias cidades sem repetir nenhuma y É um problema base para o projeto de microchips, sequênciamento de genôma e muitas outras aplicações y Não possui solução exata eficiente (Problema NP) y Utilização de heurísticas para aproximar a solução ótima y 22 ORDENS DE COMPLEXIDADE Imagine um computador que leva 1ms (0,00s) para executar uma operação. | A tabela abaixo indica o tempo aproximado de execução de um algoritmo com diferentes ordens de complexidades para 3 tamanhos de entrada | n O(n) Log2(n) nLog2(n) O(n2) O(n3) O(2n) 16 0.016s 0.004s 0.064s 0.256s 4s 32 0.032s 0.005s 0.16s 1s 33s 512 0.512s 0.009s 4.608s 4m22s 37h h O(n!) 1m5s 663 anos 49 dias 10143 sec 1023 sec ... 23 Introdução à Complexidade de Algoritmos Exemplo de estudo comparativo da complexidade de algoritmos: - Dado 3 algoritmos algoritmos, verificar erificar q qual al deles demanda maior ccusto, sto seguindo o princípio do pior caso. | Algoritmo 1: Soma de vetores: for(i=0;i<n;i++){ for(i 0;i n;i ){ S[i] = X[i] + Y[i]; } f(x) = n*(1) => f(x) = O(n) E ã complexidade Então, l id d lilinear (O( (O(n)) )) n( n*( 1 ) Introdução à Complexidade de Algoritmos Exemplo de estudo comparativo da complexidade de algoritmos - continuação | Algoritmo 2: Soma de matrizes: for(i=0;i<n;i++){ n*( for(j=0;j<n;j++){ for(j 0;j n;j ){ n*(( n C[i,j] =A[i,j] + B[i,j]; 1 } ) } ) g(x) = n*(n*(1)) => h(x) = O(n2) Então, grau de complexidade quadrática (O(n2)) Introdução à Complexidade de Algoritmos Exemplo de estudo comparativo da complexidade de algoritmos - continuação | Algoritmo 3: Produto de matrizes: f (i 0 i< i++){ for(i=0;i<n;i++){ for(j=0;j<n;j++){ P[i,j] =0; for(k=0;k<n;k++){ ( ; ; ){ P[i,j] = P[i,j] + A[i,k] * B[k,j] } } } n*( *( n*( 1+ n*(( 1 ) ) ) h(x) = n*(n*(1+n*(1))) = n*(n*(1+n)) = n*(n+n2) = n*(n2)=>h(x)= ) >h(x) O(n3) Então, grau de complexidade cúbica (O(n3)) Introdução à Complexidade de Algoritmos Exemplo de estudo comparativo da complexidade de algoritmos - continuação | Comparando agora os três algoritmos dos exemplos anteriores, temos: f(x)+g(x)+h(x) f( )+ ( )+h( ) = max(O(n),O(n (O( ) O( 2),O(n ) O( 3)) | Dessa forma, concluímos que o algoritmo que teve o maior custo foi o do exemplo 3 (h(x) = O(n3)), pois o expoente do grau de complexidade (n) é o maior i é o maior i d deles l ((expoente t 3) 3). | Então, seu grau de complexidade resultante é cúbico. | 27 Introdução à Complexidade de Algoritmos Exercícios | 1. Informe a complexidade dos algoritmos abaixo: i t a,b, int b aux; scanf(“%d%d”,&a,&b); aux = a; a = b; b = aux; printf(“%d , %d” %d”,a,b); a b); Introdução à Complexidade de Algoritmos Exercícios 2. float media, notas[100]; int ii, soma = 0 0, media; for(i=0;i<=100;i++) { printf(“entre com a nota”); scanf(“%d”,&notas[i]); soma = soma + notas[i]; } media ed a = so soma/100; a/ 00; printf(“A media é: %d”, media); Introdução à Complexidade de Algoritmos Exercícios 3. for(i=0;i<=N_LIN1;i++) { for(j=0;j<=N_COL2;j++) { C[i,j] = 0; for(k=0;k<=N_COL1;k++) { C[i,j] = C[i,j] + A[i,k] * B[k,j]; } } } Introdução à Complexidade de Algoritmos Exercícios 4. for(j=1;j<=n;j++){ for(k=1;k<=n;k++){ p=p+1; } } Introdução à Complexidade de Algoritmos Exercícios | Faça agora um estudo comparativo e determine qual ou quais dos 4 algoritmos do exercícios anterior tem a maior complexidade Introdução à Complexidade de Algoritmos Exercícios Prova de concursos: (ENADE-2005-Q15) - Considere o algoritmo que implementa o seguinte processo: uma coleção desordenada de elementos é dividida em duas metades e cada metade é utilizada como argumento t para a reaplicação li ã recursiva i d do procedimento. di t O Os resultados das duas reaplicações são, então, combinados pela intercalação dos elementos de ambas ambas, resultando em uma coleção ordenada. Qual é a complexidade desse algoritmo? (A) O(n2) (B) O(n2n) (C) O(2n) (D) O(log n × log n) (E) O(n × log n) | UNIDADE II Introdução à Complexidade p de Algoritmos g FIM