Análise de Algoritmos

Ronaldo Cristiano Prati

Bloco A, sala 513-2

ronaldo.prati@ufabc.edu.br

Análise assintótica

(recapitulando)

  • Ao ver expressões como n+10n+10 ou n2+1n^2 + 1 pensamos geralmente em valores pequenos de nn

  • A análise algoritmica faz justamente o contrário: ignora valores pequenos e concentra-se nos valores enormes de nn

  • Esse tipo de análise se chama análise assintótica.

Notação assintótica

(recapitulando)

  • Ferramenta para analisar algoritmos e descrever a ordem de crescimento dos tempos de execução

  • Provê formalismo e vocabulário matemático que nos permitem argumentar sobre a qualidade e eficiência de algoritmos.

Notação assintótica O,Ω,ΘO, \Omega, \Theta

(recordando)

  • T(n)=O(g(n))T(n) =O(g(n)) se existem constantes positivas CC e n0n_0 tais que T(n)Cg(n)T(n)\leq C g(n) para todo nn0n\geq n_0;

  • T(n)=Ω(g(n))T(n) = \Omega(g(n)) se existem constantes positivas cc e n0n_0 tais que cg(n)T(n)c g(n)\leq T(n) para todo nn0n\geq n_0

  • Dizemos que T(n)=Θ(g(n))T(n) = \Theta(g(n)) se, e somente se:

    • T(n)=O(g(n))T(n) = O(g(n)) e
    • T(n)=Ω(g(n))T(n) = \Omega(g(n))

Notação assintótica

Big-O Big-Omega Big-Theta

Notação assintótica

(recapitulando)

  • Para valores enormes de nn, as funções:
    n2n^2, 32n2\frac{3}{2}n^2 , 9999n29999 n^2 , n21000\frac{n^2}{1000}, n2+100nn^2 + 100n ,
    crescem todas com velocidades muito parecidas e portanto são todas "equivalentes"
  • Esse tipo de análise, interessado somente em valores enormes de nn, é chamado assintótica. Nessa análise, as funções são classificadas em "ordens"; todas as funções de uma mesma ordem são "equivalentes"
  • As cinco funções acima, por exemplo, pertencem à mesma ordem de crescimento

Notação assintótica o,ωo, \omega

  • T(n)=o(g(n))T(n) =o(g(n)) se existem constantes positivas cc e n0n_0 tais que 0T(n)<cg(n)0 \leq T(n) < c g(n) para todo nn0n\geq n_0;

  • T(n)=ω(g(n))T(n) = \omega(g(n)) se existem constantes positivas CC e n0n_0 tais que T(n)>Cg(n)T(n) > Cg(n) para todo nn0n\geq n_0

Notação assintótica o,ωo, \omega

  • Por exemplo, 2n=o(n2)2n = o(n^2), mas 2n22n^2 não é o(n2)o(n^2)
    • Se T(n)=o(g(n))T(n)= o(g(n)), então T(n)T(n) é insignificante com relação a g(n)g(n), para nn grande. Alternativamente, T(n)=o(g(n))T(n)= o(g(n)) quando

limnT(n)g(n)=0\lim_{n\rightarrow \infty} \frac{T(n)}{g(n)} = 0

  • Por exemplo, 2n2=ω(n)2n^2 = \omega(n), mas 2n22n^2 não é ω(n2)\omega(n^2)
    • Se T(n)=ω(g(n))T(n)= \omega(g(n)), então g(n)g(n) é insignificante com relação a T(n)T(n), para nn grande. Alternativamente, T(n)=ω(g(n))T(n)= \omega(g(n)) quando

limnT(n)g(n)=\lim_{n\rightarrow \infty} \frac{T(n)}{g(n)} = \infty

Notação assintótica

(Exemplo 1)

  • Mostre que T(n)=i=0kainiT(n) = \sum_{i=0}^k a_i n^i em que aia_i são constante para i0..ki \in 0..k é O(nk)O(n^k)

  • Escolha n0=1n_0 =1 e c=i=0kaic = \sum_{i=0}^k |a_i|. Temos que mostrar que n1,T(n)cnk\forall n \geq 1, T(n) \leq c n^k

T(n)aknk++a1n+a0aknk++a1nk+a0nk=cnk\begin{aligned} T(n)& \leq |a_k|n^k + \cdots + |a_1|n + |a_0| \\ & \leq |a_k|n^k + \cdots + |a_1|n^k + |a_0|n^k \\ & = c n^k \end{aligned}

Notação assintótica

(Exemplo 2)

  • Mostre que para todo k>1,T(n)=nkk>1, T(n) = n^k não é O(nk1)O(n^{k-1})

  • Por contradição, suponha que nk=O(nk1)n^k = O(n^{k-1}). Então, c,n0>0\exists c, n_0 > 0 tal que
    nkcnk1,nn0n^k \leq c n^{k-1}, \forall n \geq n_0

  • Cancelando nk1n^{k-1} de ambos os lados:
    nc,nn0n \leq c, \forall n \geq n_0

  • O que é claramente falso (contradição).

Notação assintótica

(Exemplo 3)

  • Mostre que T(n)=2n+10T(n) = 2^{n+10} é O(2n)O(2^n)

  • Temos que escolher constantes nn, n0n_0 tal que

2n+10c2n,n>n02^{n+10} \leq c 2^n, \forall n > n_0

  • Observe que:

2n+10=2102n=10242n2^{n+10} = 2^{10} \cdot 2^n = 1024\cdot 2^n

  • Basta escolhermos c=1024c=1024 e n0=1n_0=1

Notação assintótica

(Exemplo 4)

  • Mostre que T(n)=210nT(n) = 2^{10n} não é O(2n)O(2^n)
  • Por contradição. Se 210neˊO(2n)2^{10n} é O(2^n), então c,n0>0\exists c, n_0 > 0 tal que
    -Temos que escolher constantes nn, n0n_0 tal que
    210nc2n,n>n02^{10n} \leq c 2^n, \forall n > n_0
  • Entretanto, cancelando 2n2^n de ambos os lados
    29nc,n>n02^{9n} \leq c , \forall n > n_0
  • Que é obviamente falso

Notação assintótica

(Exemplo 5)

  • Para cada par de funções positivas f(n)f(n) e g(n)g(n), mostrar que
    max[f(n),g(n)]=Θ(f(n)+g(n))\max [f(n),g(n)] = \Theta (f(n)+ g(n))
  • Para todo nn temos que:
    max[f(n),g(n)]f(n)+g(n)\max [f(n),g(n)] \leq f(n)+ g(n)
  • e
    2max[f(n),g(n)]f(n)+g(n)max[f(n),g(n)]12(f(n)+g(n))\begin{aligned} 2 \max [f(n),g(n)] & \geq f(n)+ g(n) \\ \max [f(n),g(n)] & \geq \frac{1}{2}( f(n)+ g(n)) \end{aligned}
  • Então
    12(f(n)+g(n))max[f(n),g(n)]f(n)+g(n),n1\frac{1}{2}( f(n)+ g(n)) \leq \max [f(n),g(n)] \leq f(n)+ g(n), \forall n \geq 1
  • Portanto max[f(n),g(n)]=Θ(f(n)+g(n))\max [f(n),g(n)] = \Theta (f(n)+ g(n)), bastando escolher c=12c=\frac{1}{2} e C=1C=1, para n0=1n_0=1

Notação assintótica

(Exemplo 6)

  • Seja T(n)=10n+3lognT(n)= 10n+3\log n, mostre que T(n)=o(n2)T(n)=o(n^2)

  • Precisamos mostrar que 10n+3logn<cn2,n>n010n+3\log n < cn^2, \forall n>n_0.

  • Observe que 10n+3logn<13n10n+3\log n < 13 n
    10n+3logn<cn213n<cn2\begin{aligned} 10n+3\log n & < cn^2 \\ 13n & < cn^2 \end{aligned}

  • Quando n>13c,c,13n<cn2n> \frac{13}{c}, \forall c, 13n < c n^2, portanto T(n)=o(n2)T(n)=o(n^2) com n0=13/c+1n_0= 13/c + 1

Notação assintótica

Algumas classes de ordem mais comuns são:

Função g(n)g(n) Classe
1 Constante
logn\log n Logarítmica
nn Linear
nlognn \log n Loglinear
n2n^2 Quadrática
n3n^3 Cúbica
2n2^n Exponencial
n!n! Fatorial

Classes de problemas mais comuns

  • Constante T(n)=O(1)T(n) = O(1) - número fixo de operações

  • Logarítmica T(n)=O(logn)T(n) = O(\log n) - ocorre tipicamente em algoritmos que dividem um problema grande em dois menores, e assim sucessivamente. Se n é mil, log2n10\log_2 n \approx 10, se n é um milhão, log2n20\log_2 n \approx 20 (a base do algoritmo importa pouco).
  • linear T(n)=O(n)T(n) = O(n) - algumas operações (fixas) são executadas para cada elemento. Cada vez que nn dobra de tamanho, o tempo de execução também dobra

Classes de problemas mais comuns

  • log-linear T(n)=O(nlogn)T(n) = O(n \log n) - ocorre tipicamente quando um problema é quebrado em problemas menores, sendo que cada um deles é resolvido independentemente e depois juntando soluções. Quando nn é um milhão, nlog2nn \log_2 n é 20 milhões. Para nn 2 milhões, nlog2nn \log_2 n é 42 milhões
  • quadrático T(n)=O(n2)T(n) = O(n^2) - caso típico é um laço dentro do outro. Se nn é mil, o número de operações é 1 milhão. Quando nn dobra, o número de operações é multiplicado por 4
  • cúbico T(n)=O(n3)T(n) = O(n^3) - caso típico são três laços aninhados. Se nn é cem, o número de operações é 1 milhão. Quando nn dobra, o número de operações é multiplicado por 8

Classes de problemas mais comuns

  • exponencial T(n)=O(2n)T(n) = O(2^n) - usa força bruta para resolver um problema, e não tem aplicações práticas para nn grande. Se n=20n=20, são necessárias na ordem de 1 milhão de operações. Quando nn dobra, o tempo de execução é elevado ao quadrado
  • fatorial T(n)=O(n!)T(n) = O(n!) força bruta, mas muito pior que O(2n)O(2^n). Se n = 20, 20! = 2432902008176640000, se n = 40, 40! = 815915283247897734345611269596115894272000000000, se n = 60, 60! = 832098711274139014427634118322336438075417 2606361245952449277696409600000000000000

Classes de complexidade

Função Computador Atual Computador 100x mais rápido Computador 1000x mais rápido
log10n\log_{10} n N1N_1 103N110^3 N_1 104N110^4 N_1
nn N2N_2 100N2100 N_2 1000N21000 N_2
nlog10nn\log_{10} n N3N_3 50N350 N_3 333N3333 N_3
n2n^2 N4N_4 10N410 N_4 31.6N431.6 N_4
n3n^3 N5N_5 4.6N54.6 N_5 10N510 N_5
2n2^n N6N_6 N6+6N_6 + 6 N6+10N_6 + 10

Classes de Complexidade

Complexidade do problema versus complexidade do algoritmos

  • Em análise de algoritmos, podemos fazer a análise de duas maneiras distintss:
    • Análise de um algoritmo em particular: Quantos recursos (tempo, memória) o algoritmo X precisa para rodar, em função do tamanho dos dados de entrada
    • Análise de um problema: Em que procuramos pelo limite inferior de recursos para resolver um determinado problema

Complexidade do problema versus complexidade do algoritmos

  • Em alguns casos, as duas coisas estão ligadas: mesmo sem um resultado teórico, existe o algoritmo AA para um problema, que consome xx recursos, e AA é o melhor algoritmo conhecido para aquele problema. Ou seja, não existe (ou é muito difícil) uma análise teórica, mas conhecemos um algorithmo que resolva com recursos xx)
  • Também existem casos em que existe um limite inferior, mesmo que não se conheça um algoritmo com o desempenho daquele limite inferior (multiplicação de matrizes é um exemplo). Nesse caso, existe um resultado teórico para o problema, mas um algoritmo que consumo no máximo aquele limite teórico não é conhecido

Limite superior

  • A notação OO também é utilizada para indicar limites superiores para problemas

  • Por exemplo, dado o problema de multiplicação de duas matrizes n×nn\times n

    • Conhecemos um algoritmo para resolver esse problema (pelo método trivial) de complexidade O(n3)O(n^3)

Cota superior

  • A cota superior é a complexidade do melhor algoritmo conhecido para aquele problema

Analogia com record mundial

  • A cota superior para um problema é análogo ao recorde mundial para uma modalidade do atletismo

  • Ele é estabelecido pelo melhor atleta (algoritmo) do momento

  • Assim como o record mundial, a cota superior pode ser melhorada por algoritmo (atleta) mais veloz.

Limite inferior

  • Às vezes é possível demonstrar que, para um problema, qualquer que seja o algoritmo, o problema requer pelo menos um certo número de operações.
  • Para o problema de multiplicação de matrizes, apenas ler os elementos leva tempo O(n2)O(n^2)
  • Assim, uma cota inferior trivial é Ω(n2)\Omega(n^2)

Algoritmo Ótimo

  • Se um algoritmo tem uma complexidade igual à cota inferior do problema, ele é assintoticamente ótimo (ou simplesmente ótimo)