Cap 2
Cap 2
Cap 2
2. Algoritmos
Na matemática discreta abordamos muitos tipos de problemas. Em muitos deles, para chegarmos
à solução, temos que seguir um procedimento que, num número finito de passos, conduz à tão
desejada solução. A uma tal sequência chama-se algoritmo6 . Um algoritmo é um procedimento
para resolver um problema num número finito de passos.
• Compare o inteiro seguinte na sequência com o máximo temporário, e se for maior, tome
o máximo temporário igual a esse inteiro.
• Pare quando chegar ao fim da sequência. O máximo temporário será então o maior inteiro
da sequência.
Abreviadamente:
É claro que um algoritmo pode ser formulado explicitamente numa qualquer linguagem de
computação, mas nesse caso só poderemos utilizar expressões válidas dessa linguagem. Exempli-
fiquemos isso convertendo cada linha do algoritmo acima em código Maple7 . Podemos considerar
uma lista de números como um vector (matriz). Para usar esta funcionalidade no Maple temos
primeiro que abrir a package de Álgebra Linear linalg:
6 O termo algoritmo deriva do nome al-Khowarizmi de um matemático persa do século IX, cujo livro sobre
numerais hindus esteve na base da notação decimal moderna que hoje utilizamos.
7 Se estiver interessado no programa de cálculo simbólico Maple recomendamos a leitura do manual Maple
37
Estruturas Discretas 2.1. Algoritmos e sua complexidade
Continuando:
Fica assim traduzido o algoritmo em Maple. Dando como input uma qualquer sequência t
• Precis~
ao: os passos do algoritmo têm que estar definidos com precisão.
• Realizável: deve ser possı́vel realizar cada passo do algoritmo em tempo útil.
38
Estruturas Discretas 2.1. Algoritmos e sua complexidade
Uma definição recursiva exprime o valor de uma função num inteiro positivo em termos dos
valores da função em inteiros mais pequenos. Isto significa que podemos ter sempre um algoritmo
recursivo para calcular o valor de uma função definida por recursão. Por exemplo:
Também não é difı́cil especificar um algoritmo recursivo para o cálculo do máximo divisor
comum de dois inteiros:
Há outro modo de calcular a função potência f (n) = an a partir da sua definição recursiva:
em vez de reduzir sucessivamente o cálculo a inteiros mais pequenos, podemos começar com o
valor da função em 1 e aplicar sucessivamente a definição recursiva para encontrar os valores
da função em números sucessivamente maiores. Tal procedimento diz-se iterativo. Por outras
palavras, para calcular an usando um processo iterativo, começamos em 1 e multiplicamos
sucessivamente por cada inteiro positivo ≤ n:
39
Estruturas Discretas 2.1. Algoritmos e sua complexidade
Se simularmos este algoritmo para os primeiros 200 inteiros e contarmos o número de iterações
necessárias para levar a função até 1 obtemos a seguinte tabela:
40
Estruturas Discretas 2.1. Algoritmos e sua complexidade
Desde o valor inicial 27 até 1 a função atinge um pico na 77a iteração com o valor 9232. Grafi-
camente:
Eficiência de algoritmos. Além de produzir uma solução satisfatória e precisa para o problema
que pretende resolver, um algoritmo tem que ser eficiente (em termos de velocidade de execução).
Um dos objectivos da algoritmia consiste em medir a eficiência de algoritmos. Muitas vezes
dispomos de diferentes algoritmos que resolvem correctamente o problema, mas algum poderá
41
Estruturas Discretas 2.1. Algoritmos e sua complexidade
ser mais eficiente que os outros. Uma medida de eficiência será, claro, o tempo dispendido por
um computador para resolver o problema executando o algoritmo.
Seja P um problema e A um algoritmo para resolver P . O tempo de execução de A pode
ser analisado contando o número de determinadas operações que são efectuadas durante a sua
execução. Esta contagem pode depender do tamanho do input.
2(n − 1) + 1 = 2n − 1
comparações.
(2) Se P consiste em verificar se um determinado objecto pertence a uma dada lista, será também
natural contar o número de comparações efectuadas por A, que dependerá do tamanho da lista.
(3) Para resolver o problema da ordenação de listas existem diversos algoritmos de ordenação,
entre os quais o chamado algoritmo da inserção. A ideia por detrás deste algoritmo consiste em
dividir a lista L que se pretende ordenar em duas sublistas. A sublista L1 inclui os elementos
de L já ordenados e a sublista L2 , que é um sufixo da lista inicial, inclui os elementos de L
ainda não analisados. Cada passo do algoritmo consiste na inserção do primeiro elemento de L2
ordenadamente na lista L1 e, claro, na sua remoção da lista L2 . O algoritmo inicia-se com
L1 = {P rimeiro[L]} e L2 = Resto[L]
e termina quando L2 = ∅. No caso dos algoritmos de ordenação é tı́pico tomar-se como medida
de eficiência o número de comparações entre elementos. Claro que o número de comparações
depende da lista dada inicialmente.
Simulemos o algoritmo para a lista inicial
[3,8,6,2,5,4,10,9,7,1]
As seguintes figuras dão uma visão dinâmica da ordenação sucessiva efectuada pelo algoritmo.
As bolas verdes correspondem a elementos já ordenados (ou seja, elementos da sublista L1 ),
enquanto que as vermelhas correspondem aos elementos ainda não ordenados da sublista L2 .
42
Estruturas Discretas 2.1. Algoritmos e sua complexidade
3, 8, 6, 2, 5, 4, 10, 9, 7, 1 3, 8, 6, 2, 5, 4, 10, 9, 7, 1
3, 6, 8, 2, 5, 4, 10, 9, 7, 1 2, 3, 6, 8, 5, 4, 10, 9, 7, 1
2, 3, 5, 6, 8, 4, 10, 9, 7, 1 2, 3, 4, 5, 6, 8, 10, 9, 7, 1
43
Estruturas Discretas 2.1. Algoritmos e sua complexidade
2, 3, 4, 5, 6, 8, 10, 9, 7, 1 2, 3, 4, 5, 6, 8, 9, 10, 7, 1
2, 3, 4, 5, 6, 7, 8, 9, 10, 1 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
• situação média.
Um input no caso da pior situação é um input que leva A a executar o maior número de
operações. No caso da determinação do elemento máximo de uma sequência (exemplo (1)),
fixado o comprimento n da sequência, a pior situação acontece para qualquer lista de números
(pois o número de comparações é constante, igual a 2n − 1). No exemplo (2) a pior situação
será uma lista que não contém o objecto procurado.
No caso do algoritmo de inserção (exemplo (3)), a pior situação é quando a lista dada está
ordenada por ordem inversa.
A análise na situação média obriga a considerações probabilı́sticas pois é necessário atribuir
a cada situação uma probabilidade. Muitas vezes considera-se que as situações têm todas a
mesma probabilidade (a distribuição das mesmas é então uniforme).
44
Estruturas Discretas 2.1. Algoritmos e sua complexidade
Estudemos um pouco o caso da pior situação. Seja TA (n) o tempo de execução máximo de
A para inputs de tamanho n. A função TA chama-se a função da pior situação para A. Um
algoritmo A para resolver um problema P diz-se optimal na pior situação se qualquer algoritmo
B que resolve P satisfaz
Uma árvore de decisão para um algoritmo é uma árvore cujos nós representam pontos de
decisão no algoritmo e cujas folhas representam os resultados.
Teste. Dado um conjunto de nove moedas, uma das quais é mais pesada que as outras, use
uma balança de dois pratos (sem pesos) para determinar a moeda mais pesada.
1 2 3 4 Hu 5678
HH
H
12 u 34
u 5 6 Hu 7 8
H
@ P =9
@
@ @
1 u 2 3 @u 4 5 u 6 7 @u 8
A A A A
A A A A
u
Au u Au u Au u Au
P =1 P =2 P =3 P =4 P =5 P =6 P =7 P =8
Esta árvore tem profundidade três8 , o que significa que, neste algoritmo, o caso da pior
situação são 3 pesagens.
Será o algoritmo optimal na pior situação? Em cada pesagem pode acontecer uma de três
coisas: o prato da esquerda está mais pesado, o prato da direita está mais pesado ou os pratos
estão equilibrados. Portanto, neste tipo de problemas com balanças, as árvores de decisão são
ternárias (em cada nó haverá no máximo três ramos). Uma árvore ternária de profundidade d
tem, no máximo, 3d folhas. Como há 9 possı́veis resultados, então
Portanto, poderá haver, eventualmente, algum algoritmo cuja árvore tenha profundidade 2.
E, de facto, há:
8 A profundidade de uma árvore é o comprimento do maior caminho desde a raiz da árvore até às folhas. Na
árvore em questão, desde a raiz até às folhas há caminhos com um ramo (no caso da folha P = 9) ou três ramos
(nas restantes folhas). Mais tarde, quando estudarmos os grafos, estudaremos as árvores com mais cuidado.
45
Estruturas Discretas 2.1. Algoritmos e sua complexidade
u456
1 2 3H
HH
H
HH
u3 u 9 4 Hu 6
H
H
1 7
JJ
JJ
JJ
J
J
J
J
J
J
u
u Ju u
u Ju u
u Ju
P =1 P =2 P =3 P =7 P =8 P =9 P =4 P =5 P =6
Pela discussão acima podemos agora concluir que este é um algoritmo optimal na pior si-
tuação (2 pesagens).
Teste. Dado um conjunto de nove moedas, uma das quais é defeituosa (mais pesada ou mais
leve que as outras), determine um algoritmo optimal na pior situação para balanças de dois
pratos (sem pesos) que determine a moeda defeituosa e dê como output se a moeda é mais
pesada ou mais leve.
Uma árvore de decisão ternária com profundidade d terá no máximo 3d folhas, donde 3d ≥ 18,
isto é,
d ≥ log3 18 ≥ dlog3 18e = 3.
(A função d−e : R → N nos números reais é a chamada função tecto (ceiling) e aplica cada
número real x no menor inteiro dxe tal que dxe ≥ x. De modo análogo, podemos definir a
função chão (floor) b−c : R → N que a cada real x faz corresponder o maior inteiro bxc ≤ x.
Por exemplo, b 12 c = 0, d 12 e = 1, b− 12 c = −1, d− 12 e = 0.)
Portanto, qualquer algoritmo que resolva o problema terá sempre que efectuar pelo menos 3
pesagens. No algoritmo seguinte esse valor é igual a 3:
1 2!
3 !a u 456
! aa
! ! aa
!! aa
! aa
u u 1 2 3au
! ! a
1 2 3 7 8 9
! 7 8 Q7 8 9
@ Q
@ Q
@ Q
Q
1 u
3 4 u 6 7 u 9 7 u 9 7
@@ u 9 4 u 6 1Qu 3
Q
B B B B B B
B B B B B B
B B B B B B
u
u BBu u
u BBu u
u u
BBu u BBu u
u BBu u
u BBu
1P 2P 3P 6L 5L 4L 7P 8L 9L 9P 8P 7L 4P 5P 6P 3L 2L 1L
46
Estruturas Discretas 2.1. Algoritmos e sua complexidade
> ha := time():
> Funcaoqualquer(x):
> time() - ha;
Vamos agora usar estas funções para comparar dois algoritmos (ver Exercı́cios 11 e 12 da
ficha prática) que calculam o valor de um polinómio p(x) = a0 + a1 x + a2 x2 + · · · + an xn num
ponto especı́fico c, ou seja, o número p(c) = a0 + a1 c + a2 c2 + · · · + an cn . Os valores de entrada
são o número c e a lista de coeficientes a0 , a1 , a2 , . . . , an do polinómio.
Por exemplo, para o polinómio p(x) = 4 + 3x + 2x2 + x3 , o valor de p(5) é igual a 194:
47
Estruturas Discretas 2.1. Algoritmos e sua complexidade
e se fizermos
obtemos a lista dos coeficientes correspondentes, que podemos usar como input nos algoritmos
Polinomio e Horner. Agora, usando as ferramentas para medir o tempo de execução, obtemos:
> ha := time():
> Horner(104567890000000.0, q2000);
0.3913255222 1027971
> ha := time():
> Polinomio(104567890000000.0, q2000);
0.3913255222 1027971
Podemos assim concluir que o método de Horner de cálculo polinomial é marginalmente mais
rápido que o método tradicional da substituição da indeterminada x pelo valor onde queremos
calcular a função polinomial.
48
Estruturas Discretas 2.1. Algoritmos e sua complexidade
Teste. Mostre que qualquer função polinomial p com coeficientes reais, dada por p(n) = at nt +
at−1 nt−1 + · · · + a1 n + a0 , é da ordem de q onde q(n) = nt .
Complexidade Terminologia
O(1) Complexidade constante
O(log n) Complexidade logarı́tmica
O(n) Complexidade linear
O(n log n) Complexidade n log n
O(nb ) Complexidade polinomial
O(bn ), onde b > 1 Complexidade exponencial
O(n!) Complexidade factorial
Qual é a complexidade do algoritmo de inserção (de ordenação de listas)? No pior caso, onde
a lista está ordenada por ordem inversa, para cada i é necessário fazer i − 1 comparações. Como
i varia de 2 até ao comprimento n da lista, temos que o número de comparações no pior caso é
igual a
Xn
(i − 1) = 1 + 2 + 3 + · · · + (n − 1),
i=2
ou seja, é dado pela soma dos n − 1 primeiros números naturais (termos de uma progressão
aritmética de razão 1):
n
X n2 − n
(i − 1) = 1 + 2 + 3 + · · · + (n − 1) = .
i=2
2
1
(n − 1)n.
2
49
Estruturas Discretas 2.2. Somatórios
Suponhamos que queremos inserir um elemento na parte da lista já ordenada (a sublista L1
com i − 1 elementos) de modo que a lista resultante com i elementos esteja ordenada. Potenci-
almente, existem i posições onde esse elemento pode ser colocado (para além das i − 1 ocupadas
pela lista dada, ainda existe a possibilidade do novo elemento ser maior que todos os outros).
Vamos impor uma hipótese probabilı́stica que consiste em assumir que todas as posições são
equiprováveis para colocar o novo elemento. Isto é, cada posição tem probabilidade 1/i.
O número de comparações necessárias se o novo elemento tiver que ser colocado na posição
k ≥ 2 é i − k + 1 e na posição k = 1 é i − 1. Logo o número médio de comparações para introduzir
o novo elemento numa das i posições é dado pelo somatório
i
!
X 1 1
(i − k + 1) + (i − 1).
i i
k=2
i−1 i−2
+
i 2
que é igual a
i2 + i − 2
.
2i
Logo, o número médio de comparações para que a lista fique ordenada é dado pelo somatório
n
X 1 1 i
− + .
i=2
2 i 2
2.2. Somatórios
50
Estruturas Discretas 2.2. Somatórios
X X X X
(5) Aditividade dos ı́ndices: ai + ai = ai + ai (sendo J um conjunto finito
i∈I i∈J i∈(I∪J) i∈(I∩J)
também).
X X
(6) Mudança de variável: af (i) = aj , para qualquer função bijectiva f : I → J; mais
i∈I j∈J
X X
aj · #(f −1 ({j})) .
geralmente, para qualquer função f : I → J, af (i) =
i∈I j∈J
P P
Teste. Mostre que 0≤i<n (ai+1 − ai )bi = an bn − a0 b0 − 0≤i<n ai+1 (bi+1 − bi ).
n
X 1 n+1 n+1
(a + ri) = a + rn (n + 1) = (2a + rn) = [a + (a + rn)]
i=0
2 2 2
n
X n
X
(a + ri) = (a + r(n − i)) (por comutatividade, com p(i) = n − i)
i=0 i=0
n
X n
X
⇔ (a + ri) = (a + rn − ri)
i=0 i=0
n
X n
X n
X
⇔ 2 (a + ri) = (a + rn − ri) + (a + ri)
i=0 i=0 i=0
n
X n
X
⇔ 2 (a + ri) = ((a + rn − ri) + (a + ri)) (por associatividade)
i=0 i=0
n
X n
X
⇔ 2 (a + ri) = (2a + rn)
i=0 i=0
n
X n
X
⇔ 2 (a + ri) = (2a + rn) 1 (por distributividade)
i=0 i=0
n
X
⇔ 2 (a + ri) = (2a + rn)(n + 1) (por progressão constante)
i=0
n
X 1
⇔ (a + ri) = (a + rn)(n + 1).
i=0
2
51
Estruturas Discretas 2.2. Somatórios
n
X n
X
= a 1+r i (por distributividade)
i=0 i=0
n n−1
!
X X
= a 1+r i+n (por progressão constante)
i=0 i=1
n2 − n
= a(n + 1) + r +n
2
n2 + n
= a(n + 1) + r .
2
n
X arn+1 − a
ari =
i=0
r−1
n
X n+1
X
ari + arn+1 = ari (por aditividade com I = {0, . . . , n} e J = {n + 1})
i=0 i=0
Xn n+1
X
⇔ ari + arn+1 = ar0 + ari (por aditividade com I = {0} e J = {1, . . . , n + 1})
i=0 i=1
Xn Xn
⇔ ari + arn+1 = ar0 + ari+1 (por mudança de variável com I = {0, . . . , n})
i=0 i=0
Xn n
X
⇔ ari + arn+1 = a + r ari (por distributividade)
i=0 i=0
n
X arn+1 − a
⇔ ari = .
i=0
r−1
52
Estruturas Discretas 2.2. Somatórios
ser atacados de forma ad hoc (esta é uma das caracterı́sticas de muitas áreas da matemática
discreta: a não existência de métodos gerais de resolução que obrigam uma abordagem ad hoc ao
problema; aqui o conhecimento e a destreza na manipulação dos diversos métodos particulares
de resolução, que poderão só funcionar em alguns casos, é crucial).
Nestas observações finais indicaremos muito resumidamente algumas das técnicas mais ele-
gantes e importantes para resolver somatórios.
Método 1: Método ad hoc. Calculando as primeiras somas parciais do somatório (a1 +a2 , a1 +
a2 + a3 , etc.), é por vezes possı́vel adivinhar a correspondente fórmula geral. A ilustração deste
método em muitos exemplos interessantes pode ser vista e experimentada (interactivamente) no
módulo Somatórios10 na página da disciplina.
A fórmula deverá depois ser confirmada com uma prova formal, para termos a certeza da
sua validade. Essa prova pode ser feita de modo análogo como fizemos nalguns exemplos acima,
usando as propriedades dos somatórios que enunciámos, ou, mais facilmente, pelo método de
indução matemática estudado na secção anterior.
Método 2: Método da perturbação. A ideia por detrás deste método é a seguinte: tentar
obter duas expressões diferentes que tenham o mesmo valor, “perturbando” levemente a soma
a calcular. Um exemplo ilustra o funcionamento deste método:
Pn 2
Suponhamos que pretendemos calcular o valor de q(n) = i=1 i . Vamos perturbar le-
vemente a sua definição e tentar escrever q(n + 1) de duas formas diferentes. Por um lado,
q(n + 1) = q(n) + (n + 1)2 e, por outro lado, por uma mudança de variável,
n
X
q(n + 1) = (i + 1)2
i=0
n
X
= (i2 + 2i + 1)
i=0
n
X n
X n
X
= i2 + 2 i+ 1
i=0 i=0 i=0
n
X
= q(n) + 2 i + (n + 1).
i=0
ou seja,
n n
X (n + 1)2 − (n + 1)
X
(n + 1)2 = 2 i + (n + 1) ⇔ i= .
i=1 i=1
2
Pn
Parece que não fizemos muitos progressos! Limitámo-nos a obter a soma i=1 i (note também
Pn−1
que temos aqui uma prova formal, rigorosa, da fórmula para i=1 i que utilizámos anterior-
mente, na página 40). Mas isto sugere imediatamente o seguinte: se perturbando um pouco a
10 www.mat.uc.pt/∼picado/ediscretas/somatorios.
53
Estruturas Discretas 2.2. Somatórios
Pn
soma q(n) dos quadrados conseguimos obter uma fórmula para i=1 i, será que perturbando a
Pn
soma c(n) = i=1 i3 dos cubos conseguimos uma fórmula para q(n)?
A fórmula de recorrência de c(n) é c(n + 1) = c(n) + (n + 1)3 e, por outro lado, por uma
mudança de variável,
n
X
c(n + 1) = (i + 1)3
i=0
n
X
= (i3 + 3i2 + 3i + 1)
i=0
n
X n
X n
X n
X
= i3 + 3 i2 + 3 i+ 1
i=0 i=0 i=0 i=0
3
= c(n) + 3q(n) + n(n + 1) + (n + 1).
2
Igualando ambas as expressões, obtemos
3
c(n) + (n + 1)3 = c(n) + 3q(n) + n(n + 1) + (n + 1),
2
ou seja,
3 3
(n + 1)3 = 3q(n) + n(n + 1) + (n + 1) ⇔ 3q(n) = (n + 1)3 − n(n + 1) − (n + 1)
2 2
2(n + 1)3 − 3n(n + 1) − 2(n + 1)
⇔ q(n) =
6
2n3 + 6n2 + 6n + 2 − 3n2 − 3n − 2n − 2
⇔ q(n) =
6
2n3 + 3n2 + n
⇔ q(n) =
6
1
⇔ q(n) = n(n + 1)(2n + 1).
6
Em www.mat.uc.pt/∼picado/ediscretas/somatorios/Matematica sem palavras files/
soma quadrados.html pode ver uma “prova” geométrica, sem palavras, desta fórmula.
n3
3
n3
Analisemos agora o erro e(n) = q(n) − 3 desta aproximação:
n3
e(n) = q(n − 1) + n2 −
3
(n − 1)3 n3 (n − 1)3
= q(n − 1) − + n2 − +
3 3 3
1
= e(n − 1) + n − .
3
54
Estruturas Discretas 2.2. Somatórios
Consequentemente,
n3 (n + 1)n n
q(n) = + − .
3 2 3
Coincide com o resultado calculado anteriormente pelo método da perturbação? Basta reduzir
ao mesmo denominador e simplificar:
n(n + 1)(2n + 1)
.
6
Sim, coincide, claro!
55