Você está na página 1de 4

Complexidade de Algoritmos


Copyright
c DCT/UFMS

Abril, 1999

1 Introdução
Analisar um algoritmo significa predizer os recursos computacionais que o algoritmo requer quando da
sua execução, como por exemplo memória e tempo computacional. Em geral, existem vários algoritmos
para solucionar um problema e a análise é capaz de identificar qual é o mais eficiente.
Particularmente estamos interessados em obter uma expressão ou fórmula matemática que represente o
tempo de execução de um algoritmo. Tal fórmula deve ser simples no sentido de omitir detalhes não
relevantes, e ao mesmo tempo deve mostrar o comportamento do algoritmo, quando executado.
Os aspectos mais importantes da análise de tempo são:

• quantidade de elementos a processar;

• forma como os elementos estão dispostos na entrada.

Geralmente escrevemos o tempo de execução de um algoritmo como uma função f (n), onde n é o tamanho
da entrada. O tamanho da entrada, por sua vez, depende do problema computacional em questão. Tal
função f deve expressar o número de operações fundamentais, ou passos, executados pelo algoritmo. Veja
a seguir o exemplo 1.

Exemplo 1 Considere o seguinte problema: dadas duas matrizes A = (aij ) e B = (bij ), ambas n × n,
determinar as matrizes C = A + B e D = A × B.
Cada um dos elementos cij e dij podem ser calculados da segunite forma:
n
X
cij = aij + bij e dij = aik · bkj .
k=1

Algoritmo Soma ( A , B )
1. para i← 1 até n faça
2. para j← 1 até n faça
3. cij ← aij + bij ;
fim-algoritmo
∗ Autores: Fábio Henrique V. Martinez e Nalvo Franco de Almeida Jr.

1
Bacharelado em Ciência da Computação - UFMS 2

O algoritmo Soma acima executa a linha 3 exatamente n2 vezes. Assim, se o custo de uma soma (que é a
operação fundamental da linha 3) for igual a uma constante c1 , então dizemos que o tempo de execução,
ou o custo do algoritmo Soma é TS (n) = c1 · n2 . Ou seja, Soma tem complexidade quadrática em n.
Algoritmo Mult ( A , B )
1. para i← 1 até n faça
2. para j← 1 até n faça
3. dij ← 0;
4. para k ← 1 até n faça
5. dij ← dij + aik · bij ;
fim-algoritmo
Suponha agora que o custo de uma multiplicação seja c2 . Então, como a linha 5 é executada n3 vezes,
temos que o custo total do algoritmo Mult é dado por TM (n) = c2 · n3 . Note que estamos considerando
de custo nulo a operação executada na linha 3 deste algoritmo.

O tempo de execução ou a complexidade de tempo de um algoritmo tem por objetivo avaliar sua eficiência.
Estamos interessados em contar o número de passos que o algoritmo executa em seu pior caso, ou seja,
para a entrada mais desfavorável. Esta complexidade fornece um limitante superior para o número de
passos que o algoritmo pode efetuar em qualquer caso. Por isso tal medida é a mais utilizada.
Em qualquer dos algoritmos vistos no exemplo 1, temos que o número depassos executados sobre qualquer
entrada de tamanho n é o mesmo. Obviamente este não é o caso geral. Considere, por exemplo, o problema
que tem como entrada as duas matrizes, como no exemplo 1, mais um parâmetro binário x. Dependendo
do valor de x, deve-se calcular a soma ou o produto das duas matrizes. Assim, deveremos levar em conta,
como parâmetro de entrada, o valor de x. Temos então um caso em que o algoritmo terá tempos diferentes
de execução, dependendo de x. O pior caso é quando o valor de x determina que o algoritmo calcule o
produto de duas matrizes e, portanto, tenha custo c · n3 , onde c é o custo de uma multiplicação.

2 Notação assintótica
O mais importante na determinação do custo de um algoritmo é a identificação do termo dominante da
expressão que descreve sua complexidade. Este termo dominante, sem as constantes multiplicativas e as
parcelas menores, descreve a ordem de crescimento assintótico desta expressão, quando consideramos o
tamanho da entrada relativamente grande.
Mais formalmente, estamos interessados em obter uma função que é um limitante superior assintótico
para a nossa expressão.

Definição 1 Para uma dada função g(n), denotamos por O(g(n)) o conjunto das funções O(g(n)) =
{f (n) : existem constantes positivas c e n0 tais que 0 ≤ f (n) ≤ cg(n), para todo n ≥ n0 }

Exemplo 2 f (n) = n2 − 1 = O(n2 ). Devemos mostrar que existem constantes positivas c e n0 tais que
0 ≤ n2 − 1 ≤ cn2 , ∀n ≥ n0 . Isto vale para c = 2 e n0 = 1. Portanto, n2 − 1 ∈ O(n2 ). Costumamos
denotar n2 − 1 = O(n2 )

Exemplo 3 f (n) = n2 − 1 = O(n3 ). Devemos mostrar que existem constantes positivas c e n0 tais que
0 ≤ n2 − 1 ≤ cn3 , ∀n ≥ n0 . Isto vale para c = 1 e n0 = 1. Portanto, n2 − 1 = O(n3 ).

Mais exemplos:
Bacharelado em Ciência da Computação - UFMS 3

(a) f (n) = 403 = O(1)


(b) f (n) = 5 + 2 log n + 3 log2 n = O(log2 n)

(c) f (n) = 5 + 2 log n + 3 log2 n = O(n)


(d) f (n) = 5 · 2n + 5n10 = O(2n )

Assim, como a notação O é útil para descrever limitantes superiores asintóticos, a notação Ω, definida a
seguir, é empregada para limitantes inferiores assintóticos.

Definição 2 Para uma dada função g(n), denotamos por Ω(g(n)) o conjunto das funções Ω(g(n)) =
{f (n) : existem constantes positivas c e n0 tais que 0 ≤ cg(n) ≤ f (n), para todo n ≥ n0 }.

Exemplo 4 f (n) = n2 − 1 = Ω(n2 ). Devemos mostrar que existem constantes positivas c e n0 tais que
0 ≤ cn2 ≤ n2 − 1, ∀n ≥ n0 . Isto vale para c = 1/2 e n0 = 2. Portanto, n2 − 1 = Ω(n2 ).

Mais exemplos:

(e) f (n) = n2 − 1 = Ω(n)

(f ) f (n) = 5 + 2 log n + 3 log2 n = Ω(log2 n)

(g) f (n) = 5 + 2 log n + 3 log2 n = Ω(log n)

(h) f (n) = 5 · 2n + 5n10 = Ω(n)

Definição 3 Se f = O(g) e f = Ω(g), então f = Θ(g).

3 Algoritmos eficientes
Um algoritmo é dito ser eficiente para um problema de tamanho n se seu tempo de execução é limitado
por um polinômio p(n). Dizemos então que o algoritmo é polinomial em n. Um algoritmo de Ω(2n ) não
é eficiente, pois a função 2n cresce mais rapidamente que qualquer polinômio em n.
Os algoritmos de soma e multiplicação de matrizes vistos na seção 1 são, portanto, eficientes. Existem,
no entanto, alguns problemas para os quais não se conhece algoritmo eficiente para resolvê-los.

4 Algorimos ótimos
Seja P um problema. Um limitante inferior para P é uma função L tal que a complexidade de pior
caso de qualquer algoritmo que resolve P é Ω(L). Isto é, todo algoritmo que resolve P executa no mı́nimo
Ω(L) passos. Assim, se existir algum algoritmo A que resolve P em tempo O(L), então A é dito ser um
algoritmo ótimo para P .
Intuitivamente, um algoritmo ótimo é aquele que apresenta a menor complexidade dentre todos aqueles
que resolvem o mesmo problema.
Bacharelado em Ciência da Computação - UFMS 4

Existem limitantes inferiores naturais para determinados problemas. Qualquer algoritmo para somar
duas matrizes n × n, por exemplo, deverá, antes de qualquer coisa, ler as matrizes. Assim, um limitante
inferior para este problema é Ω(n2 ). Portanto, podemos concluir que o algoritmo Soma visto na seção 1
é ótimo para este problema.
Um outro exemplo é o problema de multiplicação de matrizes. Novamente temos um limitante Ω(n2 ).
Por outro lado, vimos que o algoritmo Mult tem complexidade O(n3 ). A pergunta então é: Mult é um
algoritmo ótimo para o problema de multiplicação de matrizes? A princı́pio nada podemos dizer sobre
isso. No entanto, sabemos que existem algoritmos para o mesmo problema com complexidade inferior.
Portanto, poemos dizer que Mult não é ótimo.

Você também pode gostar