Recorrencias PDF
Recorrencias PDF
Recorrencias PDF
Relações de Recorrência∗
1 Introdução
Intuitivamente, a partir de uma demonstração por indução pode-se extrair
um algoritmo recursivo e, da descrição deste assim obtida, pode-se facilmente
imaginar que nasce imediatamente uma expressão recursiva para a função de
complexidade do algoritmo. O objeto de estudo dentro do corrente tópico é
justamente tais funções.
Chamamos de relação de recorrência a uma expressão recursiva para
definição uma função. Estaremos especialmente estudando formas de se
encontrarem soluções (i.é., fórmulas fechadas) para relações de recorrência.
O exemplo clássico de relação de recorrência, que aprendemos desde o
segundo grau, é a fórmula de Fibonacci:
2 Revisão de Somatórios
Quando um algoritmo apresenta construções iterativas (como laços) ou
chamadas recursivas, sua complexidade pode ser expressa como a soma dos
∗ Escriba: Fernando José Castor de Lima Filho.
1
tempos gastos em cada iteração ou em cada execução do procedimento recursivo.
Por exemplo: o laço principal do algoritmo MergeSort é responsável por
combinar duas seqüências ordenadas em uma só seqüência ordenada. Esse laço
realiza, no pior caso, n operações de comparação. Além disso, ele é executado
cada vez que o procedimento recursivo mergeSort é chamado, o que ocorre
log2 n vezes, para uma entrada de tamanho n. O número de comparações
realizadas por este algoritmo é dado pelo seguinte somatório:
log2 n
X
n.
i=1
e é interpretada como
∞
X
lim ak .
n→∞
k=1
Se o limite não existe, dizemos que a série diverge; caso contrário, que ela
converge.
Somatórios apresentam a propriedade da linearidade, ou seja, para qualquer
número real c e quaisquer seqüências finitas a1 , a2 , ..., an e b1 , b2 , ..., bn ,
n
X n
X n
X
(cak + bk ) = c ak + bk .
k=1 k=1 k=1
2
Em decorrência da propriedade de linearidade, é possı́vel colocar em
evidência termos multiplicativos que sejam independentes do somatório. Por
exemplo:
Xn n
X
(f (x)ak ) = f (x) (ak ),
k=1 k=1
onde f (x) é uma função independente de k. Manipulação similar pode ser feita
com somatórios que incorporam notação assintótica. Por exemplo:
n
X n
X
Θ(f (k)) = Θ( f (k)).
k=1 k=1
Quando a série é infinita e |c| < 1, temos uma série geométrica infinita
decrescente cuja soma é dada por:
∞
X a1
ak = .
1−c
k=1
3
2.2.3 Série Harmônica
n
X 1
= ln n + γ + O(1/n),
k
k=1
n
X
blog2 kc = (n + 1)blog2 nc − 2blog2 nc+1 + 2 ∈ Θ(n log n).
k=1
2.2.5 Produtos
Qn
O produto finito a1 · a2 · . . . · an é denotado por k=1 ak . Se n = 0, o valor
do produto é definido como 1. Às vezes é conveniente converter uma fórmula
contendo
Qn um produto
Pn em uma fórmula com um somatório usando a identidade:
log ( k=1 ak ) = k=1 log ak .
Exemplo 2
n
X
k2k = (n − 1)2n+1 + 2.
k=1
Exemplo 3
n
X
k2n−k = 2n+1 − 2 − n.
k=1
4
Se x for igual a V (m), então m é o ı́ndice procurado e a busca termina. Se
x for menor que V (m), o elemento procurado só pode se encontrar à esquerda
de V (m), i.é., na primeira metade do vetor. Podemos considerar, então, que o
vetor no qual estamos realizando a busca passa a ser aquele que vai do primeiro
elemento de V ao elemento imediatamente anterior a m e podemos repetir o
mesmo procedimento recursivamente para esse vetor menor. Similarmente, para
o caso de x ser maior que V (m).
Analisando o comportamento deste algoritmo, percebemos que ele realiza
uma comparação e, logo em seguida, passa a agir sobre um vetor que tem
metade do tamanho de V . O resultado da comparação determina se a metade
de V na qual a busca prosseguirá será a primeira ou a segunda. A complexidade
de comparações deste algoritmo é descrita, então, pela seguinte relação de
recorrência:
T (n) = T ( n2 ) + 1
= (T ( n4 ) + 1) + 1
= ((T ( n8 ) + 1) + 1) + 1
= ...
¡n¢
= (. . .((T 2k
+1) + 1) + . . . + 1) + 1
| {z }
k−1 parcelas
k
X
= 1
i=1
= k = log2 n ∈ O(log n)
5
deverı́amos fazer uma demonstração indutiva da validade da 5a¯ igualdade acima,
mas a omitiremos.
Convém frisar que trocar o termo ‘1’ por uma constante c qualquer na relação
de recorrência (1) acarretaria apenas na multiplicação do resultado por essa
constante. Neste caso, a solução da recorrência passaria a ser O(c log2 n).
Apesar de interessante como um primeiro exemplo, a relação de recorrência
apresentada em (1) é demasiado simples. Sua resolução se resume em achar o
número de parcelas da soma resultante da expansão telescópica. São comuns,
porém, casos em que há expressões mais complexas na expansão da relação
de recorrência, ao invés de apenas termos simples. Nessas situações, fechar
o somatório pode ser bem mais difı́cil. Tomemos como exemplo a seguinte
recorrência:
T (n) = T (n/2) + n
= (T (n/4) + n/2) + n
= ((T (n/8) + n/4) + n/2) + n
= ...
= (. . .((T (n/2k ) + n/2k−1 ) + n/2k−2 ) + . . . + n/2) + n
= T (1) + 2 + 4 + 8 + 16 + . . . + 2k
T (n) = 1 + 2 + 4 + 8 + 16 + . . . + 2k
Pk i
= i=0 2
Plog2 n i
= i=0 2
= 2(log2 n)+1 − 1
= 2n − 1
6
T (n ) n n
ænö ænö n n
Tç ÷ Tç ÷
è 2ø è 2ø 2 2
n n
n n n n
+
2 2 2 2
lg n
n n n n n n n n
+ + +
4 4 4 4 4 4 4 4
(...)
(d) Total: O(n lg n)
7
sempre ele leva a uma solução simples, pois podem ser muito difı́ceis as
manipulações algébricas necessárias para se obter uma forma fechada para
algumas recorrências. Nesta seção, apresentamos um método que nos permite
achar a solução de uma recorrência de maneira mais fácil, exigindo apenas um
conhecimento básico de indução matemática.
Indução matemática é uma técnica de demonstração bastante poderosa,
como já foi visto em [3]. Façamos uma rápida revisão da idéia básica da indução
forte.
Seja T um teorema que se quer provar. Suponha que T inclui um parâmetro
n que pode ter como valor qualquer número natural. Ao invés de provar
diretamente que T é verdade para todos os valores de n, provamos as seguintes
condições:
1. T é verdade para n = n0 .
2. Para n > n0 , se T é verdade para todo n0 < n, então T é verdade para n.
Como, pela hipótese de indução, temos que T (bn/2c) ≤ cbn/2c logbn/2c, então
T (bn/2c) também é menor que c(n/2) log(n/2). Conseqüentemente, podemos
2 O fato (verdadeiro) de que na maioria dos casos, pisos e tetos afetam em muito pouco o
resultado de uma relação de recorrência (e podem ser ignorados) pode ser demonstrado por
indução, mas não faremos isso nestas notas.
8
remover os pisos da expressão acima, o que nos leva à seguinte desigualdade:
9
n n
n n n 3n
2 2 2 2
lg n
n n n n n n 9n
4 4 4 4 4 4 4
(...)
Total: O(n lg 3 )
e também não influi na classe da solução, dado que esta pertence a Θ(n log n) e
n ∈ o(n log n). De maneira mais abrangente, podemos afirmar que acrescentar
termos de menor ordem aditivamente não deve mudar a intuição quanto ao
resultado de uma relação de recorrência.
A situação muda, entretanto, quando acrescentamos termos multiplicativa-
mente. A relação de recorrência
apesar de muito similar às últimas apresentadas, não tem solução pertencente a
Θ(n log n). Olhando para a árvore de recorrência correspondente a essa relação,
na figura 2, podemos perceber que a soma dos termos de cada passo resulta em
um valor maior que n por um fator multiplicativo que não é constante. De fato,
a expansão telescópica da relação de recorrência (7) tem solução pertencente a
O(nlog2 3 ).
Semelhantemente, alterar o termo pelo qual n é dividido a cada passo
também modifica a solução da recorrência. Uma árvore apresentando a expansão
da relação de recorrência
10
n n
n n 2n
3 3 3
lg n
n n n n 4n
9 9 9 9 9
(...)
Total: O (n)
que não implica que T (n) ≤ cn, qualquer que seja a escolha de c. A idéia
que surge imediatamente neste caso é tentar um palpite maior, por exemplo,
T (n) ∈ O(n2 ). Um palpite como esse funcionaria, mas não seria necessário pois
o nosso palpite inicial está quase correto, necessitando apenas de um pequeno
ajuste.
Nosso palpite se mostrou grande demais por um termo de menor ordem
(nesse caso, uma constante). Resolvemos o problema então subtraindo de nosso
palpite um termo da mesma ordem de grandeza do termo excedente. Nosso
novo palpite é, então, T (n) ≤ cn − b, onde b ≥ 0 é constante. Agora temos:
T (n) ≤ (cbn/2c − b) + (cdn/2e − b) + 1
= cn − 2b + 1
≤ cn − b,
11
para b ≥ 1.
Pode ser inicialmente contra-intuitivo considerar que se deve subtrair um
termo (ainda que de menor ordem). Afinal de contas, se o algebrismo não
funciona, não deverı́amos tentar aumentar nosso palpite? A chave para entender
este passo é lembrar que estamos usando indução matemática: podemos provar
algo mais apertado para um dado valor através da suposição de algo mais
apertado para valores menores.
É fácil errar quando usamos notação assintótica. Por exemplo, podemos
tentar provar que a relação de recorrência (4) pertence a O(n) usando o palpite
de que T (n) ≤ cn e argumentando
T (n) ≤ 2(cbn/2c) + n
≤ cn + n ∈ O(n),
uma vez que c é uma constante. O erro neste caso é que não provamos a forma
exata da hipótese indutiva, isto é, não provamos que T (n) ≤ cn. Começamos
tentando provar uma coisa (que T (n) ≤ cn) mas saltamos para uma conclusão
alternativa que não corresponde ao que pretendı́amos demonstrar. Note que
T (n) ≤ cn implica que T (n) ∈ O(n), mas, por outro lado, T (n) ≤ cn + n e
cn + n ∈ O(n) não implicam que T (n) ∈ O(n).
Algumas vezes, um pouco de manipulação algébrica pode fazer uma
recorrência aparentemente desconhecida tomar um aspecto mais familiar. Como
exemplo, considere a seguinte relação de recorrência:
√
T (n) = T (b nc) + log2 n.
T (2m ) = 2T (2m/2 ) + m.
S(m) = 2S(m/2) + m,
que é muito parecida com a recorrência (4) e tem a mesma solução: S(m) ∈
O(m log m). Mudando de volta de S(m) para T (2m ) = T (n), obtemos que
T (n) ∈ O(log n log log n).
12
Teorema 1 (Teorema Master[1])
Sejam a ≥ 1 e b ≥ 1 constantes, seja f (n) uma função e seja T (n) definida
para os inteiros não-negativos pela relação de recorrência
onde interpretamos que n/b significa bn/bc ou dn/be. Então T (n) pode ser
limitada assintoticamente da seguinte maneira:
1. Se f (n) ∈ O(nlogb a−² ) para alguma constante ² > 0, então T (n) ∈
Θ(nlogb a )
2. Se f (n) ∈ Θ(nlogb a ), então T (n) ∈ Θ(nlogb a log n)
3. Se f (n) ∈ Ω(nlogb a+² ), para algum ² > 0 e se af (n/b) ≤ cf (n),
para alguma constante c < 1 e para n suficientemente grande, então
T (n) ∈ Θ(f (n))
13
Assumindo que T (1) = c, ficamos com:
Desprezando
³ k m+1 as ´constantes na última linha da expressão acima e sabendo que
am (b (b/a) −1
k /a)−1 = bkm e bm = n, concluı́mos que T (n) ∈ Θ(nk ). CQD
Os resultados dos teoremas 1 e 2 se aplicam a uma grande quantidade de
relações de recorrência e são muito úteis na análise de eficiência de algoritmos.
14
O primeiro algoritmo que analisaremos parte do pressuposto de que sabemos
calcular o valor do polinômio Pn−1 (x) = an−1 xn−1 + an−2 xn−2 + ... + a1 x + a0 ,
o que nos deixa com o trabalho de calcular apenas Pn (x) = an · xn + Pn−1 (x).
Este último passo requer n multiplicações e uma adição. Como o número de
adições é pequeno comparado ao de multiplicações, nós o ignoraremos quando
calcularmos a complexidade de tempo do algoritmo. Neste caso, a relação de
recorrência resultante é a seguinte:
T (n) = T (n − 1) + n.
T (n) = T (n − 1) + 2.
T (n) = T (n − 1) + 1,
15
Base: para n = 1, o grafo G tem um subgrafo induzido que satisfaz o
enunciado (o próprio G) somente no caso em que k = 0.
Hipótese: Dados um grafo não-orientado G = (V, E) e um inteiro k,
sabemos encontrar um subgrafo induzido H = (U, F ) de G de tamanho máximo
tal que todos os vértices de H tenham grau ≥ k (em H), ou concluir que nenhum
tal subgrafo induzido existe, desde que o número de vértices de G seja < n.
Passo: Sejam G = (V, E) um grafo não-orientado com |V | = n e k um
inteiro. Se |V | < k + 1, nenhum vértice pode ter grau ≥ k, conseqüentemente,
não há nenhum subgrafo induzido com a propriedade desejada.
Assumamos, então, que n ≥ k + 1. Se todos os vértices de G têm grau ≥ k,
então, basta tomarmos H = G. Caso contrário, selecionamos um vértice v cujo
grau seja < k e o removemos de G, junto com todas as suas arestas incidentes. O
grafo resultante tem n − 1 vértices e, por hipótese de indução, sabemos resolver
esse problema. CQD
Complexidade: Para analisar a complexidade deste algoritmo, é necessário
formalizar um pouco mais a representação do grafo. Vamos levar em
consideração apenas as duas representações mais usuais: lista de adjacências
e matriz de adjacências.
1
3
2
4
5
Figura 4: Exemplo simples de um grafo não-orientado.
1 4
2 3 4 5
3 2
4 1 2 5
5 2 4
16
temos uma lista encadeada onde cada nó da lista corresponde a um vértice
de G adjacente a v. Veja um exemplo na figura 4 cuja representação por lista
de adjacências é apresentada na figura 5.
1 2 3 4 5
1 0 0 0 1 0
2 0 0 1 1 1
3 0 1 0 0 0
4 1 1 0 0 1
5 0 1 0 1 0
17
de grau < k, precisamos percorrer apenas uma linha (correspondente ao vértice
v) e uma coluna (correspondente aos vértices aos quais v é adjacente) da matriz.
A complexidade para esta representação é descrita pela seguinte relação de
recorrência:
T (n) = T (n − 1) + n + n.
A solução para esta recorrência pertence a O(n2 ), ou seja, a representação
da informação aumentou a velocidade do algoritmo.
É possı́vel reduzir ainda mais o tempo necessário para se encontrar os vértices
de grau menor que k através do uso de uma estrutura auxiliar como, por
exemplo, um heap [2] com a chave de acesso sendo o grau de cada vértice.
O tempo necessário para construir o heap inicial é O(n log n) e a remoção do
i-ésimo elemento do heap toma tempo O(log(n − i + 1)).
Uma importante lição que podemos tirar deste exemplo é que a eficiência
de um algoritmo é freqüentemente melhorada se se escolhe uma estrutura de
dados adequada para representação da informação de modo a facilitar os acessos
requeridos pelo algoritmo.
18
log∗2 2 = 1
log∗2 2
2 = log∗2 4 = 2
2
log∗2 22 = log∗2 24 = log∗2 16 = 3
22
log∗2 22 = log∗2 216 = log∗2 65536 = 4
2 22
log∗2 22 = log∗2 265536 = 5
2
22
Exercı́cio: Na representação decimal, quantos dı́gitos tem o número 22
(que é o menor inteiro N para o qual log∗2 N é maior que 4)?
C F C F
H E D E D
H
G G
B B
(a) (b)
19
e à de busca pelo conjunto a que pertence um dado elemento retornando o
identificador do conjunto correspondente.
Uma representação que permite que realizemos eficientemente uma seqüência
destas operações (UNIÃO e BUSCA) utiliza o que chamamos de árvores
enraizadas. Cada árvore representa um conjunto e cada um de seus nós um
elemento do respectivo conjunto.
Cada nó da árvore é um registro contendo o identificador do elemento e
um apontador para seu nó pai na árvore. Ao elemento representado pelo nó
raiz designamos como representante do conjunto (e usamos seu identificador
para identificar o próprio conjunto). (O apontador do nó raiz aponta para si
mesmo.) A figura 7(a) apresenta dois conjuntos representados desta maneira
e a figura 7b) mostra o conjunto resultante da aplicação de uma operação de
união.
Para o Problema da UNIÃO e BUSCA de conjuntos, nosso objetivo é
fazer com que as operações de UNIÃO e de BUSCA possam ser rapidamente
executadas. Para alcançarmos esse objetivo, lançamos mão de duas heurı́sticas
que diminuem substancialmente o tempo de execução dessas duas operações.
D
C D C
B A
B
A
(a) (b)
20
em [1]. Uma análise mais acurada (veja [4, 5]) mostra que uma quota superior
ainda melhor é possı́vel, explicitamente, O(m α(m, n)), onde α(m, n) é a inversa
da função de Ackermann (descrita em [1]). A função α(m, n) cresce ainda mais
lentamente que a função log∗ n.
5 Exercı́cios Suplementares
1. Encontre um limite superior para a solução da relação de recorrência
T (n) = 168T (bn/672c + 135) + 168T (bn/672c + 135) + 168T (bn/672c +
189) + 168T (bn/672c + 189) + 456n + 1.
Referências
[1] T. Cormen, C. Leiserson, R. Rivest, C. Stein, Introduction to Algorithms,
segunda edição, MIT Press, 2001.
[2] U. Manber, Algorithms: A Creative Approach, Addison-Wesley, 1989.
[3] J. Porto, P. Rezende, Provas por Indução. Notas de Aula para a Disciplina
de Complexidade de Algoritmos, Instituto de Computação, UNICAMP,
2002.
[4] R. E. Tarjan, “Efficiency of a good but not linear set union algorithm”,
Journal of the ACM, 22(2):215-225, 1975.
[5] R. E. Tarjan, Data Structures and Network Algoritms. SIAM, 1983.
21