Notações O, Ω e Θ
Yuri Tavares dos Passos
Notação O, Ω e Θ
●
São utilizadas para denotar o
comportamento assintótico de uma função
Notação O, Ω e Θ
●
Notação O
– É usada para determinar um limite superior
assintótico.
●
f(n) O(g(n)) →
∈
●
f(n)∈{h(n) | c,N
∃ ∈ℝ: ∀n≥N: h(n) ≤
cg(n)}
●
f(n) é limitada superiormente por g(n)
Notação O, Ω e Θ
●
Também usa-se f(x) = O(g(x))
●
Assim, também podemos afirmar que f(x)
= O(g(x)) se e somente se a
∃ ∈ℝ, a ≥ 0:
lim
x→∞
f ( x)
g(x)
≥a
Notação O, Ω e Θ
●
Observe que este limite pode ser igual a 0.
Notação O, Ω e Θ
●
Exemplos:
– 5n2
+15∈O(n2
) → n
∀ ≥4: 5n2
+15 ≤ 6n2
(c=6, N=4)
– 5n2
+15∈O(n3
) → n
∀ ≥6: 5n2
+15 ≤ n3
(c=1, N=6)
– n2
∉ O(n) → c,N
∄ ∈ℝ: n
∀ ≥N n2
≤ cn
●
Suponha c,N, tais que n
∃ ∀ ≥N n2
≤ cn. Então n
∀ ≥N n2
-
cn ≤ 0. As soluções para n são 0 ≤ n ≤ c. Como n está
limitado entre 0 e c, ele não pode ser maior que
qualquer N. Contradição!
Notação O, Ω e Θ
Notação O, Ω e Θ
●
Notação Ω
– É usada para determinar um limite inferior
assintótico.
●
f(n) Ω(g(n)) →
∈
●
f(n)∈{h(n) | c,N
∃ ∈ℝ: n
∀ ≥N: h(n) ≥ cg(n)}
●
f(n) é limitada inferiormente por g(n)
Notação O, Ω e Θ
●
Também usa-se f(x) = Ω(g(x))
●
Assim, também podemos afirmar que f(x)
= Ω(g(x)) se e somente se a
∃ ∈ℝ, 0 < a
≤ ∞:
lim
x→∞
f ( x)
g(x)
=a
Notação O, Ω e Θ
●
Observe que este limite não pode ser igual
a 0.
Notação O, Ω e Θ
●
Exemplos:
– 5n2
+15∈Ω(n) → n
∀ ≥4: 5n2
+15 ≥ n (c=1, N=4)
– 5n2
+15∉Ω(n3
) → c,N
∄ ∈ℝ: n
∀ ≥N 5n2
+15 ≥
cn3
Notação O, Ω e Θ
Notação O, Ω e Θ
●
Notação Θ
– É usada para determinar uma igualdade
assintótica.
●
f(n)∈Θ(g(n)) s.s.s. f(n)∈O(g(n)) e
f(n)∈Ω(g(n))
●
f(n) é assintoticamente equivalente a g(n)
Notação O, Ω e Θ
●
Também usa-se g(n) = Θ(f(n))
●
Assim, também podemos afirmar que f(x)
= Ω(g(x)) se e somente se a
∃ ∈ℝ, 0 < a
< ∞:
lim
x→∞
f ( x)
g(x)
=a
Notação O, Ω e Θ
●
Exemplos:
– 5n2
+15∈Θ(n2
)
– 4n+1∈Θ(n)
Notação O, Ω e Θ
Propriedades
●
Seja k e p constantes e n variável.
– k O(np
) = O(np
)
– n O(nk
) = O(nk+1
)
– O(nk
) + O(np
) = O(nmax(k,p)
)
●
Ex.: O(n) + O(n4
) = O(n4
)
– O(f(n)) + O(g(n)) = O(max(f(n),g(n)))
●
Ex.: O(log(n)) + O(n) = O(n)
●
Similar para Θ.
●
Para Ω troca-se max por min.
Resumo
●
O “equivale ao” ≤.
●
Ω “equivale ao” ≥.
●
Θ “equivale ao” =.
Referência
●
HOPCROFT, John E.; ULLMAN, Jeffrey
D.; MOTWANI, Rajeev. Introdução à teoria
de autômatos, linguagens e computação.
[Rio de Janeiro]: Campus, 2003.
●
Imagens da versão em inglês
Introdução a análise de
algoritmos
Yuri Tavares dos Passos
Motivação
●
Computadores usam recursos como tempo e espaço.
●
Diferentes algoritmos podem resolver um mesmo
problema, mas é preferível aquele que use menos
recursos.
●
A análise deve ser independente da máquina, pois
operações podem levar tempos diferentes em diferentes
máquinas.
– CISC: uma soma com três operandos pode custar apenas um
ciclo.
– RISC: precisa-se mover os valores da memória para
registrador antes de somar.
Tipos de análise
●
Melhor caso
– Considera apenas o caso com menor custo.
●
Pior caso
– Similar, mas para o maior custo.
●
Caso médio
– Considera a média de todos os casos
considerando a probabilidade de cada um
deles ocorrer.
E(X)=∑
i=1
n
P( X=xi) xi
Exemplo 1
SWAP(x,y)
1 t = x
2 x = y
3 y = t
Exemplo 1
SWAP(x,y)
1 t = x
2 x = y
3 y = t
custo
c1
c2
c3
vezes
1
1
1
ci é constante (possivelmente diferente para cada i).
Exemplo 1
●
As constantes poderiam ser diferentes a
depender da máquina ou implementação.
– Se x, y e t são registradores, c1 = c2 = c3 = 1.
– Caso x, y e t estejam na memória RAM, c1 = c2
= c3 = 2.
●
Custo total é
c1 + c2 + c3 = Θ(1).
Exemplo 2
MIN(A)
1 min = A[1]
2 for i = 2 to A.length
3 if min > A[i]
4 min = A[i]
5 return min
●
Seja n o tamanho do vetor A.
Exemplo 2
MIN(A)
1 min = A[1]
2 for i = 2 to A.length
3 if min > A[i]
4 min = A[i]
5 return min
custo
c1
c2
c3
c4
c5
vezes
1
n
n-1
tA
1
tA é o número de vezes que a linha 4 é
executada para um dado vetor A.
Exemplo 2
●
Os testes dos comandos for ou while
rodam uma vez a mais antes de encerrar.
●
O custo total é
c1 + c2n + c3(n-1) + c4tA + c5
●
O pior caso ocorre quando tA = n-1.
– Ex.: A = [4 3 2 1]
●
O custo de pior caso é
●
c1 + c2n + c3(n-1) + c4(n-1) + c5 = Θ(n)
Exemplo 2
●
O melhor caso ocorre quando tA = 0.
– Ex.: A = [1 2 3 4]
●
O custo de melhor caso é
c1 + c2n + c3(n-1) + c5 = Θ(n)
●
O custo de caso médio é obtido pelo
valor esperado de tA, E[tA].
Exemplo 2
●
Considere Xi o evento que é 1 caso na
iteração i do algoritmo, o valor de min seja
modificado:
Xi=
{1,se min> A[i],
0, se min≤A[i].
Exemplo 2
A X2 X3 X4 tA
1 2 3 4 0 0 0 0
1 2 4 3 0 0 0 0
1 3 2 4 0 0 0 0
1 3 4 2 0 0 0 0
1 4 2 3 0 0 0 0
1 4 3 2 0 0 0 0
2 1 3 4 1 0 0 1
2 1 4 3 1 0 0 1
2 3 1 4 0 1 0 1
2 3 4 1 0 0 1 1
2 4 3 1 0 0 1 1
2 4 1 3 0 1 0 1
A X2 X3 X4 tA
3 1 2 4 1 0 0 1
3 1 4 2 1 0 0 1
3 2 1 4 1 1 0 2
3 2 4 1 1 0 1 2
3 4 1 2 0 1 0 1
3 4 2 1 0 1 1 2
4 1 2 3 1 0 0 1
4 1 3 2 1 0 0 1
4 2 1 3 1 1 0 2
4 2 3 1 1 0 1 2
4 3 1 2 1 1 0 2
4 3 2 1 1 1 1 3
Exemplo 2
●
Temos que
E[tA] = E[X2] + E[X3] + … + E[Xn]
...
E[X2] = 0·½ + 1·½ = ½
1 2 n
E[X3] = 0·(2/3) + 1·⅓ = ⅓
1 2 3 n
1 2 i n
E[Xi] = 1/i
Exemplo 2
E[tA] = E[X2] + E[X3] + … + E[Xn]
= 1/2 + 1/3 + … + 1/n
= Hn – 1
= Θ(ln n)
0
1
2
3
4
5
0 10 20 30 40 50
H
n
n
Exemplo 2
●
Custo de caso médio é
c1 + n c2 + (n-1) c3 + (Hn - 1) c4 + c5 =
c1 + n c2 + (n-1) c3 + Θ(ln n) c4 + c5 = Θ(n)
Exemplo 3
●
Seja n o tamanho do vetor A.
tj é o número de vezes que o teste do while na
linha 5 é executado para um dado valor j do for
na linha 1.
Exemplo 3
●
O custo total é
c1 n+c2(n−1)+c4(n−1)+c5∑
j=2
n
t j+c6 ∑
j=2
n
(t j−1)
+c7∑
j=2
n
(t j−1)+c8(n−1)
●
Melhor caso para tj = 1 com A ordenado:
c1 n+c2(n−1)+c4 (n−1)+c5(n−1)+c8(n−1)=Θ(n)
Exemplo 3
●
O pior caso com tj = j para A em ordem decrescente:
c1 n+c2(n−1)+c4(n−1)+c5 ∑
j=2
n
j+c6 ∑
j=2
n
( j−1)
+c7 ∑
j=2
n
( j−1)+c8(n−1)=
c1 n+c2(n−1)+c4(n−1)+c5(n(n+1)
2
−1)+c6(n(n−1)
2 )
+c7(n(n−1)
2 )+c8(n−1)=Θ(n2
)
Exemplo 3
●
O caso médio utiliza-se E[tj]. Para cada j, tj
pode ser um valor de 1 a j, com igual
probabilidade, 1/j.
1 2 j-1 j
... key
E[t j]=1
1
j
+2
1
j
+...+ j
1
j
=∑
t=1
j
1
j
t=
j+1
2
Exemplo 3
●
Usando E[tj] no custo total, temos:
c1 n+c2(n−1)+c4(n−1)+c5∑
j=2
n
E[t j ]+c6 ∑
j=2
n
(E[t j]−1)
+c7 ∑
j=2
n
(E[t j ]−1)+c8(n−1)=
c1 n+c2(n−1)+c4(n−1)+c5(n(n−1)
4
+n−1)+c6(n(n−1)
4 )
+c7(n(n−1)
4 )+c8(n−1)=Θ(n
2
)
Análise simplificada
●
Melhor e pior casos podem ser analisados
de forma simplificada em alguns casos
como no exemplo a seguir.
Exemplo 1 (melhor caso)
MIN(A)
1 min = A[1]
2 for i = 2 to A.length
3 if min > A[i]
4 min = A[i]
5 return min
tempo
Θ(1)
Θ(n)
Θ(n)
0
Θ(1)
Total é 2Θ(n)+2Θ(1) = Θ(n)
Exemplo 1 (pior caso)
MIN(A)
1 min = A[1]
2 for i = 2 to A.length
3 if min > A[i]
4 min = A[i]
5 return min
tempo
Θ(1)
Θ(n)
Θ(n)
Θ(n)
Θ(1)
Total é 3Θ(n)+2Θ(1) = Θ(n)
Exemplo 2 (melhor caso)
tempo
Θ(n)
Θ(n)
0
Θ(n)
Θ(n)
0
0
Θ(n)
Total é 5Θ(n) = Θ(n)
Exemplo 2 (pior caso)
tempo
Θ(n)
Θ(n)
0
Θ(n)
Θ(n2
)
Θ(n2
)
Θ(n2
)
Θ(n)
Total é 3Θ(n2
)+4Θ(n) = Θ(n2
)
Custos escondidos
●
As notações O, Ω e Θ podem esconder
custo úteis para comparação entre
algortimos de mesma ordem de
complexidade.
●
No exemplo a seguir, ambos os algoritmos
são Θ(n), mas um é Θ(2n) enquanto que o
outro é Θ(1,5n) (ou seja, mais eficiente).
Exemplo
MINMAX1(A)
1 min = max = A[1]
2 for i = 2 to A.length
3 if min > A[i]
4 min = A[i]
5 else if max < A[i]
6 max = A[i]
7 return (min, max)
MINMAX2(A)
1 if A.length mod 2 ≠ 0
2 min = max = A[1]
3 i = 2
4 else
5 if A[1] < A[2]
6 min = A[1]
7 max = A[2]
8 else
9 min = A[2]
10 max = A[1]
11 i = 3
12 while i ≤ A.length
13 if A[i] < A[i+1]
14 if A[i] < min
15 min = A[i]
16 if A[i+1] > max
17 max = A[i+1]
18 else
19 if A[i+1] < min
20 min = A[i]
21 if A[i] > max
22 max = A[i+1]
23 i = i + 2
24 return (min, max)
Exercício
●
Calcule o custo de tempo total de
execução dos algoritmos MINMAX1 e
MINMAX2. Qual é o custo no melhor e no
pior caso?
Complexidade de espaço
●
É similar a de tempo, mas as colunas
custo e vezes são substituídas pelo
consumo de memória.
●
Criação de novas variáveis tem custo
constante, mas utilização tem custo zero.
Exemplo
MIN(A)
1 min = A[1]
2 for i = 2 to A.length
3 if min > A[i]
4 min = A[i]
5 return min
custo
c1
c2
0
0
0
vezes
1
1
0
0
0
Exemplo 1
●
Custo de espaço é c1+c2=Θ(1).
●
Se min e i são inteiros de 8 bit, então o
custo de espaço da função MIN é de
apenas 16 bits.
●
Observe que foi assumido que A foi
passado previamente sem necessitar
armazenar novamente.
Exemplo 2
custo
c1
c2
c3(n1+n2+2)
c4
0
c5
0
0
0
vezes
1
1
1
1
0
1
0
0
0
Exemplo 2
custo
0
0
c6
0
0
0
0
0
vezes
0
0
1
0
0
0
0
0
Exemplo 2
●
Custo de espaço é
c1+c2+c3(n1+n2+2)+c4+c5+c6 =
c1+c2+c3(q-p+1+r-q+2)+c4+c5+c6 =
c1+c2+c3(r-p+3)+c4+c5+c6 = Θ(r-p+3).
Referência
●
Cormen, T.H., Leiserson, C.E., Rivest, R.L.
Stein, C. Introduction to Algorithms. Third
edition. 2009. MIT Press.
Teorema mestre para
recursões
Yuri Tavares dos Passos
Teorema mestre
●
Útil para calcular complexidade de
algoritmos recursivos.
●
Exemplos: quicksort e mergesort.
Mergesort
Mergesort
Mergesort
Mergesort
tempo
Θ(1)
Θ(1)
Θ(1)
Θ(n1+1)
Θ(n1)
Θ(n2+1)
Θ(n2)
Θ(1)
Θ(1)
Mergesort
tempo
Θ(1)
Θ(1)
Θ(r-p+2)
Θ(r-p+1)
Θ(tL)
Θ(tL)
Θ(r-p+1-tL)
Θ(r-p+1-tL)
Mergesort
●
tL é a quantidade de vezes que um
elemento de L foi selecionado.
●
Custo total do MERGE é
7Θ(1) + Θ(n1+1) + Θ(n1) + Θ(n2+1) + Θ(n2) +
Θ(r-p+2) + Θ(r-p+1) + 2Θ(tL) + 2Θ(r-p+1-tL) =
Θ(1) + Θ(n1) + Θ(n2) + Θ(r-p+2) + Θ(r-p+1) +
2Θ(r-p+1) =
Θ(1)+Θ(r-p+1)+Θ(r-p+1) = Θ(r-p+1),
visto que n1 + n2 = q – p + 1 + r – q = r – p + 1.
Mergesort
tempo
T(r-p+1)
Θ(1)
Θ(1)
T(q-p+1)
T(r-(q+1)+1)
Θ(r-p+1)
Mergesort
●
Se p ≥ r, temos o caso base T(i) = Θ(1)
para qualquer i ≤ 1.
●
Tipicamente, p = 1 e r = n = |A|, assim:
T(n) = T(r-p+1)
T(n) = 2Θ(1) + T(q-p+1) + T(r-(q+1)+1) + Θ(n)
T(n) = T( (n+1)/2 )+T(n-
⌊ ⌋ (n+1)/2 )+Θ(n)+Θ(1)
⌊ ⌋
T(n) = T( n/2 )+T(
⌈ ⌉ n/2 )+Θ(n),
⌊ ⌋
●
Abusando a notação, para simplificar:
T(n) = 2 T(n/2) + Θ(n), se n > 1.
Teorema mestre
Seja a ≥ 1 e b > 1, f(n) uma função e T(n)
definido sobre inteiros não negativos pela
equação recursiva
T(1) = Θ(1) e
T(n) = aT(n/b) + f(n) se n > 1.
onde n/b pode ser interpretado como n/b
⌊ ⌋
ou n/b . Então,
⌈ ⌉
Teorema mestre
1) Se f(n) = O(nlog
b
a-ε
) para ε > 0, então
T(n) = Θ(nlog
b
a
).
2) Se f(n) = Θ(nlog
b
a
), então
T(n) = Θ(nlog
b
a
log2n).
3) Se f(n) = Ω(nlog
b
a+ε
) para ε > 0 e se af(n/b) ≤
cf(n) para alguma constante c < 1 e n
suficientemente grande,
T(n) = Θ(f(n)).
Interpretação
●
Comparamos f(n) com nlog
b
a
.
– A maior delas determina a solução da
recursão.
●
Se elas são “do mesmo tamanho”,
adicione um fator logaritmo na solução.
Interpretação
●
Maior (menor) no sentido polinomial: maior
(menor) que nlog
b
a
por um fator nε
.
– Existem possibilidades entre os casos 1 e 2 e
os casos 2 e 3 que não são tratados no
teorema e não podem ser aplicados.
Exemplo 1
T(n) = 9T(n/3) + n.
●
a = 9, b = 3, f(n) = n, nlog
b
a
= nlog
3
9
= n2
.
●
f(n) = n = O(n2-1
) = O(nlog
3
9-1
) = O(nlog
a
b-ε
)
com ε = 1.
●
Pelo caso 1:
T(n) = Θ(nlog
b
a
) = Θ(n2
).
Exemplo 2
T(n) = T(2n/3) + 1.
●
a = 1, b = 3/2, f(n) = 1, nlog
b
a
= nlog
(3/2)
1
=
n0
=1.
●
f(n) = 1 = Θ(1) = Θ(n0
) = Θ(nlog
(3/2)
1
).
●
Pelo caso 2:
T(n) = Θ(nlog
b
a
log2n) = Θ(log2n).
Exemplo 3
T(n) = 3T(n/4) + n log2n.
●
a = 3, b = 4, f(n) = n log2n, nlog
b
a
= nlog
4
3
= O(n0,793
).
●
f(n) = n log2n = Ω(n) e n = Ω(n0,993
) =
Ω(n0,793+0.2
)=Ω(nlog
4
3+0.2
)=Ω(nlog
a
b+ε
) com ε=0,2.
●
Para n grande, a f(n/b) = 3(n/4)log2(n/4) ≤ (3/4)n
log2n = c f(n) para c = 3/4.
●
Pelo caso 3:
T(n) = Θ(f(n)) = Θ(n log2n).
Exemplo 4
T(n) = 2T(n/2) + n log2n.
●
a = 2, b = 2, f(n) = n log2n, nlog
b
a
= nlog
2
2
= n.
●
f(n) = n log2n = Ω(n) porém não existe ε >
0 tal que
f(n) = n log2n = Ω(nlog
b
a+ε
) = Ω(n1+ε
)
●
pois log2n = O(nε
) para qualquer ε > 0.
●
Este exemplo está entre os casos 2 e 3.
De onde vem o nlog
b
a
?
Exercícios
1) Use o teorema mestre para obter o custo
de tempo e espaço do mergesort.
2) Calcule a complexidade de tempo e
espaço do quicksort no melhor e pior caso.
Exercícios
Referência
●
Cormen, T.H., Leiserson, C.E., Rivest, R.L.
Stein, C. Introduction to Algorithms. Third
edition. 2009. MIT Press.
Corretude de algoritmos
Yuri Tavares dos Passos
Motivação
●
Verificação automática de código por
asserções (comando assert de algumas
linguagens).
●
Exemplo:
Pré-condição e pós-
condição
●
São proposições lógicas que devem ser
satisfeitas antes e depois da execução de
um comando.
●
Notação:
{P} Pré-condição
S Comando
{Q} Pós-condição
Exemplos
{0≤a∧0≤b}
z = a*b
{z = ab}
{x = X y = Y
∧ }
SWAP(x,y)
{x = Y y = X
∧ }
{V}
i = 1
{i = 1}
Verdadeiro
i = 1
{i = 1}
Omissão
significa
hipótese
verdadeira
Provas usando pré- e
pós-condições
●
Assuma que P P
⇒ 1 e Q Q
⇒ 1. Então
também temos:
Justificativa
{P}
{P1} [P P
⇒ 1]
S
{Q}
{Q1} [Q Q
⇒ 1]
Exemplo
●
Provar que SWAP troca os elementos.
{x = X y = Y
∧ }
SWAP(x,y)
1 t = x
2 x = y
3 y = t
{x = Y y = X
∧ }
Exemplo
{x = X y = Y
∧ }
SWAP(x,y)
{x = X y = Y
∧ }
1 t = x
{t = X∧x = X y = Y
∧ }
2 x = y
{t = X∧x = Y y = Y
∧ }
3 y = t
{t = X∧x = Y y = X
∧ }
{x = Y y = X} [P Q Q
∧ ∧ ⇒ ]
Atribuições
{P}
i = 1
{i = 1 P
∧ }
●
A atribuição de uma constante a uma
variável adiciona uma igualdade a pré-
condição na pós-condição:
Atribuições
{i=4}
{i+1=5}
i = i+1
{i=5}
●
Atribuições de expressões devem
substituir a expressão pela variável na
pós-condição.
{i≥0}
{i+1≥1}
i = i+1
{i≥1}
{i>0}
Atribuições
{i≥0 s=1+2+...+i
∧ }
{i+1>0 s=1+2+...+
∧ i+1-1} [álgebra]
i = i+1
{i>0 s=1+2+...+
∧ i-1}
{i>0 s+i=1+2+...+i
∧ } [+i ambos lados]
s = s+i
{i>0 s=1+2+...+i
∧ }
Seleção
●
Nos comandos if … else, a condição do if
é adicionada com e lógico a pré-condição
na pós-condição dentro do if. No else, a
negação da condição é adicionada dentro
do else. {P}
if cond
{cond P
∧ }
S1
{cond P Q
∧ ∧ }
else
{¬cond P
∧ }
S2
{¬cond P Q
∧ ∧ }
Seleção
●
Se o comando if não possuir um else
associado, deve-se analisar como se
existisse um else seguido de nenhum
comando (ou skip).
{P}
if cond
{cond P
∧ }
S1
{cond P Q
∧ ∧ }
//else
// skip
{¬cond P
∧ }
Exemplo
●
Provar que o algoritmo a seguir calcula |x|.
{x=X}
if x<0
x=-x
{x=|X|} |x|={ x ,x≥0
−x , x<0
Exemplo
●
Assuma que há um else skip.
{x=X}
if x<0
x=-x
else
skip
{x=|X|}
|x|={ x ,x≥0
−x , x<0
Exemplo
{x=X}
if x<0
{x=X x<0}
∧
{-x=-X X<0} [(-1)x=(-1)X e x=X em x<0]
∧
x=-x
{x=-X X<0}
∧
{x=|X|} [def. |X|]
else
{x=X x
∧ ≥0}
skip
{x=X x
∧ ≥0}
{x=X X
∧ ≥0} [x=X em x≥0]
{x=|X|} [def. |X|]
{x=|X|}
Laços
●
Para o comando while, a condição do
comando é adicionada a pós-condição
depois da condição. No fim do while, a
negação da condição é adicionada a pós-
condição do último commando no while.
●
Para comando for é similar:
for i = s to t
S
i = s
while (i≤t)
S
i = i + 1
⇔
Exemplo 1
●
Mostre que o seguinte algoritmo soma todos os
elementos de um vetor de tamanho n≥0 antes de
retornar.
{n≥0}
SUM(A)
1 i = 1
2 s = 0
3 while i ≤ n
4 s = s+A[i]
5 i = i+1
6 return s
{s=∑
k=1
n
A[k]
}
Exemplo 1
{n≥0}
SUM(A)
{n≥0}
1 i = 1
2 s = 0
{n+1≥1 i=1 s = 0} [+1 em n
∧ ∧ ≥0]
[i em n+1≥1, def. ∑]
3 while i ≤ n
{i≤n∧i≤n+1∧ s=∑
k=1
i−1
A[k]}
{i≤n+1∧ s=∑
k=1
i−1
A[k]}
Exemplo 1
{i≤n+1∧ s=∑
k=1
i−1
A[k]}
[SIMP ]
∧
4 s = s+A[i]
5 i = i+1
// fim do while
{i+1≤n+1∧s+A[i]=∑
k=1
i
A[k]}
{i>n∧i≤n+1∧s=∑
k=1
i−1
A[k]}
{i≤n∧ s=∑
k=1
i−1
A[k]} {i=n+1∧ s=∑
k=1
i−1
A[k]}
6 return s
{s=∑
k=1
n
A[k]}
Exemplo 2
●
Mostre que o seguinte algoritmo calcula o
menor elemento num vetor de tamanho
n≥1 antes de retornar.
{n≥1}
MIN(A)
1 min = A[1]
2 for i = 2 to n
3 if min > A[i]
4 min = A[i]
{ k : 0<k
∀ ∈ℕ ≤n⇒min≤A[k]}
5 return min
Exemplo 2
MIN(A)
1 min = A[1]
2 for i = 2 to n
3 if min > A[i]
4 min = A[i]
5 return min
MIN(A)
1 min = A[1]
2 i = 2
2 while i ≤ n
3 if min > A[i]
4 min = A[i]
5 i = i + 1
6 return min
Exemplo 2
{n≥1}
MIN(A)
{n≥1}
{n+1≥2}
1 min = A[1]
2 i = 2
{n+1≥2∧i = 2 min = A[1]}
∧
{i≤n+1 i=2 k : 0<k
∧ ∧∀ ∈ℕ ≤1 min
⇒ ≤A[k]}
{i≤n+1∧ k : 0<k
∀ ∈ℕ ≤i-1 min
⇒ ≤A[k]}
2 while i ≤ n
{i≤n i
∧ ≤n+1∧∀k : 0<k
∈ℕ ≤i-1 min
⇒ ≤A[k]}
{i≤n k : 0<k
∧∀ ∈ℕ ≤i-1 min
⇒ ≤A[k]}
3 if min > A[i]
Exemplo 2
3 if min > A[i]
{min>A[i] i
∧ ≤n∧∀k : 0<k
∈ℕ ≤i-1 min
⇒ ≤A[k]}
{i≤n∧ k : 0<k
∀ ∈ℕ ≤i-1 A[i]<min
⇒ ≤A[k]}
{i≤n∧ k : 0<k
∀ ∈ℕ ≤i-1 A[i]
⇒ ≤A[k]}
{i≤n A[i]
∧ ≤A[i] k : 0<k
∧∀ ∈ℕ ≤i-1 A[i]
⇒ ≤A[k]} [A[i]≤A[i]]
{i≤n k : 0<k
∧∀ ∈ℕ ≤i A[i]
⇒ ≤A[k]}
4 min = A[i]
{i≤n k : 0<k
∧∀ ∈ℕ ≤i min
⇒ ≤A[k]}
//else skip
{min≤A[i] i
∧ ≤n∧ k : 0<k
∀ ∈ℕ ≤i-1 min
⇒ ≤A[k]}
{i≤n k : 0<k
∧∀ ∈ℕ ≤i min
⇒ ≤A[k]}
{i+1≤n+1 k : 0<k
∧∀ ∈ℕ ≤i+1-1 min
⇒ ≤A[k]}
5 i = i + 1
{i≤n+1 k : 0<k
∧∀ ∈ℕ ≤i-1 min
⇒ ≤A[k]}
//fim do while
{i>n i
∧ ≤n+1 k : 0<k
∧∀ ∈ℕ ≤i-1 min
⇒ ≤A[k]}
Exemplo 2
{i>n i
∧ ≤n+1 k : 0<k
∧∀ ∈ℕ ≤i-1 min
⇒ ≤A[k]}
{i=n+1 k : 0<k
∧∀ ∈ℕ ≤i-1 min
⇒ ≤A[k]}
{ k : 0<k
∀ ∈ℕ ≤n min
⇒ ≤A[k]}
6 return min
Invariante de laço
●
É uma condição que deve existir antes de um
laço começar e no fim dele, antes de retornar
ao teste do laço.
●
O objetivo dele é se tornar o resultado
desejado quando alcançar o final do
algoritmo.
●
A invariante de laço também deve ser
verdadeira após os comandos de
inicializaçao antes do laço.
Exemplo 1
SUM(A)
1 i = 1
2 s = 0
3 while i ≤ n
4 s = s+A[i]
5 i = i+1
{i≤n+1∧ s=∑
k=1
i−1
A[k]}
{i≤n+1∧ s=∑
k=1
i−1
A[k]}
6 return s
{i>n∧i≤n+1∧s=∑
k=1
i−1
A[k]}
{s=∑
k=1
n
A[k]}
Exemplo 2
MIN(A)
1 min = A[1]
2 for i = 2
{i≤n+1∧ 0<k
∀ ≤i-1: min≤A[k]}
to n
3 if min > A[i]
4 min = A[i]
// depois de i = i + 1
{i≤n+1 0<k
∧∀ ≤i-1: min≤A[k]}
//fim do for
{i>n i
∧ ≤n+1 0<k
∧∀ ≤i-1: min≤A[k]}
{ 0<k
∀ ≤n: min≤A[k]}
5 return min
Exemplo 3
MULTIPLICAR(a,b)
1 x, y, z = a, b, 0
2 while y > 0
3 if y mod 2 = 0
4 y, x = y div 2, x+x
5 else
6 y, z = y-1, z+x
7 return z
Exemplo 3
{0≤b}
MULTIPLICAR(a,b)
1 x, y, z = a, b, 0
{0≤y z+xy = ab
∧ }
2 while y > 0
3 if y mod 2 = 0
4 y, x = y div 2, x+x
{0≤y z+xy = ab
∧ }
5 else
6 y, z = y-1, z+x
{0≤y z+xy = ab
∧ }
// fim do while
{y=0 0
∧ ≤y z+xy = ab}
∧
{z = ab}
7 return z
Exercício
●
Apresente as demais pré- e pós condições
ao longo do algoritmo anterior de forma a
provar as invariantes de laço após as
linhas 1, 4, 6 e 7.
Prova de corretude em
linguagem natural
●
A prova de corretude em linguagem
natural é escrita em cima das pré- e pós-
condições verificadas pelos comandos do
algoritmo junto com a invariante de laço.
●
Deve-se enunciar a invariante de laço e
provar as seguintes três partes:
Prova de corretude em
linguagem natural
●
Inicialização: consiste em demostrar que a
invariante de laço é verdadeira antes de iniciar
o laço e após os comandos anteriores ao laço.
●
Manuntenção: mostra que a invariante se
mantém no início e no fim do laço antes de
retornar a condição de teste do laço.
●
Término: prova que a invariante junto com a
negação da condição do laço estabelece o
objetivo final do algoritmo.
Exemplo 1
{n≥0}
SUM(A)
1 i = 1
2 s = 0
3 while i ≤ n
4 s = s+A[i]
5 i = i+1
6 return s
{i≤n+1∧s=∑
k=1
i−1
A[k ]}
{i≤n+1∧s=∑
k=1
i−1
A[k ]}
{i>n∧i≤n+1∧s=∑
k=1
i−1
A[k ]}
{s=∑
k=1
n
A[k]}
Prova:
A invariante de laço é a seguinte:
“para cada laço, indexado por i, i ≤ n+1 e .”
Inicialização: Antes da linha 3,
e i ≤ n+1, pois i = 1 e n ≥ 0⇔n+1 ≥ 1 = i.
Manuntenção: Após a linha 4, temos Depois
da linha 5, i torna-se i+1, assim s passa a ser e i ≤
n+1, mantendo a invariante de laço no fim do laço.
Término: Após o comando enquanto, antes da linha 6, i > n
e i ≤ n+1. Logo, i = n+1 e
s=∑
k=1
i−1
A[k]
s=0=∑
k=1
0
A[k]=∑
k=1
i−1
A[k]
s=∑
k=1
i
A[k].
∑
k=1
i−1
A[k]
s=∑
k=1
n
A[k].
Exemplo 2
MIN(A)
1 min = A[1]
2 for i = 2 to n
3 if min > A[i]
4 min = A[i]
5 return min
MIN(A)
1 min = A[1]
2 i = 2
{invariante de laço}
3 while i ≤ n
4 if min > A[i]
5 min = A[i]
6 i = i + 1
{invariante de laço}
7 return min
Exemplo 2
●
A invariante de laço é
i≤n+1 k : 0<k≤i-1 min≤A[k]
∧
∀ ∈ ⇒
‘
no final, mas depois do incremento de i.
MIN(A)
1 min = A[1]
2 for i = 2 to n
3 if min > A[i]
4 min = A[i]
5 return min
Exemplo 2
{n≥1}
MIN(A)
1 min = A[1]
2 for i = 2
{i≤n+1∧ k : 0<k
∀ ∈ℕ ≤i-1 min
⇒ ≤A[k]}
to n
3 if min > A[i]
4 min = A[i]
{i≤n+1 k : 0<k
∧
∀ ∈ℕ ≤i-1 min
⇒ ≤A[k]}
// depois de i = i + 1
{i≤n+1 k : 0<k
∧
∀ ∈ℕ ≤i-1 min
⇒ ≤A[k]}
//fim do for
{i>n i
∧≤n+1 k : 0<k
∧
∀ ∈ℕ ≤i-1 min
⇒ ≤A[k]}
{ k : 0<k≤n min≤A[k]}
∀ ∈ℕ ⇒
5 return min
Prova:
A invariante de laço é para cada
iteração de número i, i ≤ n+1 e, para
todo k natural com 0<k≤i-1, min≤A[k].
Inicialização: Depois de inicializar i
com 2 na primeira iteração do for na
linha 2, como n≥1, então i≤n+1. Como
min=A[1] da linha 1, então para todo k
satisfazendo 0<k≤i-1=1, min≤A[k].
Manuntenção: Após o if na linha 3,
min>A[i]. Assim, pela invariante de
laço, para todo k natural com 0<k≤i-1,
A[i]<min≤A[k]. Como A[i]≤A[i], têm-se,
portanto, que para todo k natural com
0<k≤i, A[i]≤A[k]. Após a linha 4,
min=A[i]. Então, para todo k natural
com 0<k≤i, min≤A[k] após a linha 4.
Exemplo 2
Caso a condição no if da linha 4 não
seja verdade, então min≤A[i]. Logo,
para todo k natural com 0<k≤i,
min≤A[k] antes da linha 5. Após a
linha 5, a invariante se mantém,
visto que i = i+1-1 e substituindo
nesta expressão i+1 por i, têm-se
que para todo k natural com 0<k≤i-1,
min≤A[k].
Término: ao final do comando
while, antes da linha 6, têm-se que
i>n. Como a invariante afirma que
i≤n+1, então i = n+1. Assim, como a
invariante também afirma que para
todo k natural com 0<k≤i-1,
min≤A[k], portanto, para todo k
natural com 0<k≤n, min≤A[k].
{n≥1}
MIN(A)
1 min = A[1]
2 for i = 2
{i≤n+1∧ k : 0<k
∀ ∈ℕ ≤i-1 min
⇒ ≤A[k]}
to n
3 if min > A[i]
4 min = A[i]
{i≤n+1 k : 0<k
∧
∀ ∈ℕ ≤i-1 min
⇒ ≤A[k]}
// depois de i = i + 1
{i≤n+1 k : 0<k
∧
∀ ∈ℕ ≤i-1 min
⇒ ≤A[k]}
//fim do for
{i>n i
∧≤n+1 k : 0<k
∧
∀ ∈ℕ ≤i-1 min
⇒ ≤A[k]}
{ k : 0<k≤n min≤A[k]}
∀ ∈ℕ ⇒
5 return min
Exercício
{0≤b}
MULTIPLICAR(a,b)
1 x, y, z = a, b, 0
{0≤y z+xy = ab
∧ }
2 while y > 0
3 if y mod 2 = 0
4 y, x = y div 2, x+x
{0≤y z+xy = ab
∧ }
5 else
6 y, z = y-1, z+x
{0≤y z+xy = ab
∧ }
{y=0 0
∧ ≤y z+xy = ab}
∧
{z = ab}
7 return z
Faça a prova em linguagem natural.
Laços aninhados
●
Haverá invariantes de laço para cada laço,
desde o mais externo até o mais interno.
Exemplo 1
●
Prove que o algoritmo a seguir soma duas
matrizes A e B com resultado na matriz C.
{0≤n 0≤m
∧ }
1 for i=1 to n
2 for j=1 to m
3 C[i,j] = A[i,j] + B[i,j]
{ k,l : 1≤k≤n 1≤l≤m C[k,l]=A[k,l]+B[k,l]
∀ ∈ℕ ∧ ⇒ }
Exemplo 1
1 i = 1
2 while i≤n
3 j = 1
4 while j≤m
5 C[i,j] = A[i,j] + B[i,j]
6 j = j+1
7 i = i + 1
1 for i=1 to n
2 for j=1 to m
3 C[i,j] = A[i,j]
+ B[i,j]
Exemplo 1
{0≤n 0≤m}
∧
1 i = 1
{0≤n 0≤m i=1}
∧ ∧
{i≤n+1 0≤m} [0≤n 1≤n+1, i=1≤n+1]
∧ ⇒
{i≤n+1 0≤m
∧ ∧
∀k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]}
∈ℕ ∧ ⇒
[Verdadeiro por vacuidade]
2 while i≤n
{i≤n i≤n+1 0≤m
∧ ∧ ∧
∀k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]}
∈ℕ ∧ ⇒
{i≤n 0≤m
∧ ∧ k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]}
∀ ∈ℕ ∧ ⇒
[SIMP ]
∧
3 j = 1
{j=1 i
∧ ≤n 0≤m
∧ ∧ k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]
∀ ∈ℕ ∧ ⇒
+B[k,l]}
Exemplo 1
{j=1 i
∧ ≤n 0≤m
∧ ∧ k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]}
∀ ∈ ∧ ⇒
{j=1 i
∧ ≤n 0≤m k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]
∧ ∧∀ ∈ ∧ ⇒
ℕ ∧
∀l : 1≤l≤0 C[i,l]=A[i,l]+B[i,l]} [Verdadeiro por vacuidade]
∈ℕ ⇒
{i≤n j≤m+1 k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]
∧ ∧∀ ∈ ∧ ⇒
ℕ ∧
∀l : 1≤l≤j-1 C[i,l]=A[i,l]+B[i,l]}
∈ℕ ⇒
[0≤m 1≤m+1, j=1≤m+1,j-1=0
⇒ ]
4 while j≤m
{j≤m i
∧ ≤n j≤m+1 k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]
∧ ∧∀ ∈ ∧ ⇒ ∧
ℕ
∀l : 1≤l≤j-1 C[i,l]=A[i,l]+B[i,l]}
∈ℕ ⇒
{j≤m i
∧ ≤n k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]
∧∀ ∈ ∧ ⇒ ∧
ℕ
∀l : 1≤l≤j-1 C[i,l]=A[i,l]+B[i,l]} [SIMP ]
∈ℕ ⇒ ∧
5 C[i,j] = A[i,j] + B[i,j]
{j≤m i
∧ ≤n k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]
∧∀ ∈ ∧ ⇒ ∧
ℕ
∀l : 1≤l≤j-1 C[i,l]=A[i,l]+B[i,l] C[i,j] = A[i,j] + B[i,j]}
∈ℕ ⇒ ∧
{j≤m i
∧ ≤n k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]
∧∀ ∈ ∧ ⇒ ∧
ℕ
∀l : 1≤l≤j C[i,l]=A[i,l]+B[i,l]} [incluído no último , de 1 até j]
∈ℕ ⇒ ∀
6 j = j+1
Exemplo 1
{j≤m i
∧ ≤n k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]
∧∀ ∈ ∧ ⇒ ∧
ℕ
∀l : 1≤l≤j C[i,l]=A[i,l]+B[i,l]} [incluído no último , de 1 até j]
∈ℕ ⇒ ∀
6 j = j+1
{j≤m+1 i
∧ ≤n k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]
∧∀ ∈ ∧ ⇒ ∧
ℕ
∀l : 1≤l≤j-1 C[i,l]=A[i,l]+B[i,l]}
∈ℕ ⇒
//fim do while j
{j>m j
∧ ≤m+1 i
∧ ≤n k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]
∧∀ ∈ ∧ ⇒ ∧
ℕ
∀l : 1≤l≤j-1 C[i,l]=A[i,l]+B[i,l]}
∈ℕ ⇒
{j=m+1 i
∧ ≤n k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]
∧∀ ∈ ∧ ⇒ ∧
ℕ
∀l : 1≤l≤j-1 C[i,l]=A[i,l]+B[i,l]}
∈ℕ ⇒
{i≤n k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]
∧∀ ∈ ∧ ⇒ ∧
ℕ
∀l : 1≤l≤m C[i,l]=A[i,l]+B[i,l]}
∈ℕ ⇒
{i≤n k,l : 1≤k≤i 1≤l≤m C[k,l]=A[k,l]+B[k,l]}
∧∀ ∈ ∧ ⇒
ℕ
[último incluído no anterior, de 1 até i]
∀ ∀
7 i = i + 1
{i≤n+1 k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]}
∧∀ ∈ ∧ ⇒
ℕ
//fim do while i}
{i>n i
∧ ≤n+1 k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]}
∧∀ ∈ ∧ ⇒
ℕ
Exemplo 1
{i>n i
∧ ≤n+1 k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]}
∧∀ ∈ ∧ ⇒
ℕ
{i=n+1 k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]}
∧∀ ∈ ∧ ⇒
ℕ
{ k,l : 1≤k≤n 1≤k≤m C[k,l]=A[k,l]+B[k,l]
∀ ∈ℕ ∧ ⇒ }
Exemplo 1
●
A invariante de laço principal é
i ≤ n+1 k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]
∧∀ ∈ℕ ∧ ⇒
+B[k,l]
1 for i=1 to n
2 for j=1 to m
3 C[i,j] = A[i,j] + B[i,j]
‘
no final, mas depois do incremento de i.
Exemplo 1
●
A invariante do laço interno é
j ≤ m+1 k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]
∧∀ ∈ℕ ∧ ⇒
+B[k,l] l : 1≤l≤j-1 C[i,l]=A[i,l]+B[i,l]
∧∀ ∈ℕ ⇒
1 for i=1 to n
2 for j=1 to m
3 C[i,j] = A[i,j] + B[i,j]
‘
no final, mas depois do incremento de j e
antes do incremento de i.
Exemplo 1
Prova:
A invariante do laço principal é
“para i ≤ n+1, para todo k, l , com 1≤k≤i-1 e 1≤l≤m, C[k,l]=A[k,l]+B[k,l].”
∈ℕ
Inicialização: Após i=1 na linha 1, a invariante se mantém, pois nenhum
elemento foi adicionado e o intervalo 1≤k≤i-1 é vazio. Além disso, como 0≤n,
i=1 implica que i≤n+1.
Manutenção: Após o teste do for na linha 1, i≤n. Para o while com j, temos a
seguinte invariante de laço:
"j≤m+1 e para todo k,l com 1≤k≤i-1 e 1≤l≤m, C[k,l]=A[k,l]+B[k,l] e para todo
∈ℕ
l com 1≤l≤j-1, C[i,l]=A[i,l]+B[i,l]."
∈ℕ
Inicialização do laço interno: Após j=1 da linha 2, como 0≤m, temos que
j≤m+1. Como j=1, por vacuidade, temos que para todo
para todo l com 1≤l≤j-1=0, C[i,l]=A[i,l]+B[i,l].
∈ℕ
Exemplo 1
Manuntenção do laço interno: No teste do for na linha 2, temos j≤m. Após a
linha 3, C[i,j] = A[i,j] + B[i,j] pode ser incluso na proposição para todo l com
∈ℕ
1≤l≤j-1, C[i,l]=A[i,l]+B[i,l], resultando em para todo l com 1≤l≤j, C[i,l]=A[i,l]
∈ℕ
+B[i,l]. Após o incremento de j no for para j, a invariante do laço interno se
mantém.
Término do laço interno: Após o for da linha 2, antes do incremento do for para
i, j=m+1. Assim, a invariante do laço interno torna-se para todo k,l com 1≤k≤i-
∈ℕ
1 e 1≤l≤m, C[k,l]=A[k,l]+B[k,l] e para todo l com 1≤l≤m, C[i,l]=A[i,l]+B[i,l]. Esta
∈ℕ
é equivalente a para todo k,l com 1≤k≤i e 1≤l≤m, C[k,l]=A[k,l]+B[k,l], pois o
∈ℕ
resultado para a linha i da matriz C é incluído no primeiro para todo que variava
de 1 a i-1.
Continuando o a manuntenção da invariante de laço principal, o resultado acima
torna-se a invariante de laço principal após o incremento de i no for mais
externo.
Exemplo 1
Término: Após o fim do for mais externo, i = n + 1 e a invariante de laço
principal torna-se o resultado desejado, isto é, para todo k,l com 1≤k≤n e
∈ℕ
1≤l≤m, C[k,l]=A[k,l]+B[k,l].
Exemplo 2
●
Demonstre que o INSERTION-SORT
ordena um vetor de tamanho n≥1.
{n≥1}
INSERTION-SORT(A)
1 for j = 2 to n
2 key = A[j]
3 i = j - 1
5 while i > 0 and A[i] > key
6 A[i + 1] = A[i]
7 i = i - 1
8 A[i + 1] = key
{ k : 0<k<n A[k] ≤ A[k+1]}
∀ ∈ℕ ⇒
Exemplo 2
INSERTION-SORT(A)
1 for j = 2 to A.length
2 key = A[j]
3 i = j - 1
5 while i > 0 and A[i] > key
6 A[i + 1] = A[i]
7 i = i - 1
8 A[i + 1] = key
INSERTION-SORT(A)
0 j = 2
1 while j ≤ A.length
2 key = A[j]
3 i = j - 1
5 while i > 0 and A[i] > key
6 A[i + 1] = A[i]
7 i = i - 1
8 A[i + 1] = key
9 j = j + 1
Notação simplificada:
∀k : 0<k<n P(k)
∈ℕ ⇒ ⇔ 0<k<n P(k)
∀ ⇒
Exemplo 2
{n≥1}
INSERTION-SORT(A)
{n≥1}
1 j = 2
{n+1≥2 j=2 1≤k≤0: A[k]≤A[k+1]} [Verdadeiro por vacuidade]
∧ ∧∀
{0<j≤n+1 1≤k≤j-2: A[k]≤A[k+1]} [0<2=j, j-2=0]
∧∀
2 while j≤n
{0<j≤n+1 j≤n 1≤k≤j-2: A[k]≤A[k+1]}
∧ ∧∀
{0<j≤n 1≤k≤j-2: A[k]≤A[k+1]}
∧∀
{j≤n 0<j 1≤k≤j-2: A[k]≤A[k+1]
∧ ∧∀ ∧
A[j]≤A[j] j≤k≤j-1: A[k]=A[k+1]} [a≤a, Verdadeiro por vacuidade]
∧∀
3 key = A[j]
4 i = j - 1
{j≤n -1<i 1≤k≤i-1: A[k]≤A[k+1] key≤A[i+1]
∧ ∧∀ ∧ ∧
∀i+1≤k≤j-1: A[k]=A[k+1]}
{j≤n 0≤i 1≤k≤i-1: A[k]≤A[k+1] key≤A[i+1]
∧ ∧∀ ∧ ∧ [-1<i 0≤i]
⇒
∀i+1≤k≤j-1: A[k]=A[k+1]}
Exemplo 2
5 while i>0 and A[i]>key
{j≤n i>0 A[i]>key 0≤i 1≤k≤i-1: A[k]≤A[k+1]
∧ ∧ ∧ ∧∀ ∧
key≤A[i+1] i+1≤k≤j-1: A[k]=A[k+1]}
∧∀
{j≤n 0<i A[i]>key 1≤k≤i-2: A[k]≤A[k+1] A[i-1]≤A[i]
∧ ∧ ∧∀ ∧ ∧
∀i+1≤k≤j-1: A[k]=A[k+1]}
[i>0 0≤i→0<i, SIMP , A[i-1]≤A[i] extraído do ]
∧ ∧ ∀
{j≤n 0<i A[i]>key 1≤k≤i-2: A[k]≤A[k+1] i+1≤k≤j-1: A[k]=A[k+1]}
∧ ∧ ∧∀ ∧∀
[SIMP ]
∧
6 A[i+1] = A[i]
{j≤n 0<i A[i+1]>key 1≤k≤i-2: A[k]≤A[k+1] A[i]=A[i+1]
∧ ∧ ∧∀ ∧ ∧
∀i+1≤k≤j-1: A[k]=A[k+1]}
{j≤n 0<i key≤A[i+1] 1≤k≤i-2: A[k]≤A[k+1]
∧ ∧ ∧∀ ∧ i≤k≤j-1: A[k]=A[k+1]}
∀
[a<b→a≤b]
7 i = i – 1
{j≤n -1<i key≤A[i+2] 1≤k≤i-1: A[k]≤A[k+1]
∧ ∧ ∧∀ ∧
∀i+1≤k≤j-1: A[k]=A[k+1]}
{j≤n 0≤i key≤A[i+2] 1≤k≤i-1: A[k]≤A[k+1] A[i+1]=A[i+2]
∧ ∧ ∧∀ ∧ ∧
∀i+2≤k≤j-1: A[k]=A[k+1]} [-1<i→0≤i, A[i+1]=A[i+2] extraído do ]
∀
Exemplo 2
{j≤n 0≤i key≤A[i+2] 1≤k≤i-1: A[k]≤A[k+1] A[i+1]=A[i+2]
∧ ∧ ∧∀ ∧ ∧
∀i+2≤k≤j-1: A[k]=A[k+1]}
{j≤n 0≤i 1≤k≤i-1: A[k]≤A[k+1] key≤A[i+1] i+1≤k≤j-1: A[k]=A[k+1]}
∧ ∧∀ ∧ ∧∀
[key≤A[i+2] A[i+1]=A[i+2]→key≤A[i+1]]
∧
//fim do while com i
{¬(i>0 A[i]>key) j≤n 0≤i 1≤k≤i-1: [k]≤A[k+1]
∧ ∧ ∧ ∧∀ ∧
key≤A[i+1] i+1≤k≤j-1: A[k]=A[k+1]}
∧∀
{(i≤0 A[i]≤key) j≤n 0≤i 1≤k≤i-1: A[k]≤A[k+1]
∨ ∧ ∧ ∧∀ ∧
key≤A[i+1] i+1≤k≤j-1: A[k]=A[k+1]}
∧∀
{j≤n (
∧ (i≤0 0≤i 1≤k≤i-1: A[k]≤A[k+1] key≤A[i+1]
∧ ∧∀ ∧ ∧
∀i+1≤k≤j-1: A[k]=A[k+1])∨(A[i]≤key 0≤i 1≤k≤i-1: A[k]≤A[k+1]
∧ ∧∀ ∧
key≤A[i+1] i+1≤k≤j-1: A[k]=A[k+1]
∧∀ ))}
{j≤n (
∧ (i=0 1≤k≤i-1: A[k]≤A[k+1] key≤A[i+1]
∧∀ ∧ ∧
∀i+1≤k≤j-1: A[k]=A[k+1])∨(0≤i 1≤k≤i-1: A[k]≤A[k+1]
∧∀ ∧
A[i]≤A[i+1] i+1≤k≤j-1: A[k]=A[k+1])
∧∀ )}
{j≤n (
∧ (key≤A[1] 1≤k≤j-1: A[k]=A[k+1])
∧∀ ∨ 1≤k≤j-1: A[k]≤A[k+1]
∀ )}
[subst. i=0, SIMP ,a=b→a≤b
∧ ]
8 A[i+1] = key
{j≤n ((i=0
∧ A[i+1]≤A[1] 1≤k≤j-1: A[k]=A[k+1]) 1≤k≤j-1: A[k]≤A[k+1])}
∧ ∧∀ ∨∀
Exemplo 2
{j≤n ((i=0 A[i+1]≤A[1] 1≤k≤j-1: A[k]=A[k+1]) 1≤k≤j-1: A[k]≤A[k+1])}
∧ ∧ ∧∀ ∨∀
{j≤n 1≤k≤j-1: A[k]≤A[k+1]} [i=0 A[i+1]≤A[1]→V,a=b→a≤b]
∧∀ ∧
9 j = j+1
{j≤n+1 1≤k≤j-2: A[k]≤A[k+1]}
∧∀
{j>n j≤n+1 1≤k≤j-2: A[k]≤A[k+1]}
∧ ∧∀
{j=n+1 1≤k≤j-2: A[k]≤A[k+1]}
∧∀
{ k : 0<k<n A[k]≤A[k+1]}
∀ ∈ ⇒
ℕ
Exemplo 2
INSERTION-SORT(A)
1 for j = 2 to A.length
2 key = A[j]
3 i = j - 1
5 while i > 0 and A[i] > key
6 A[i + 1] = A[i]
7 i = i - 1
8 A[i + 1] = key
INSERTION-SORT(A)
0 j = 2
{invariante de laço}
1 while j ≤ A.length
2 key = A[j]
3 i = j - 1
5 while i > 0 and A[i] > key
6 A[i + 1] = A[i]
7 i = i - 1
8 A[i + 1] = key
9 j = j + 1
{invariante de laço}
Exemplo 2
●
A invariante de laço principal é
j ≤ n+1 0<k<j-1 A[k] ≤ A[k+1]
∧ ∀ ⇒
INSERTION-SORT(A)
1 for j = 2 to A.length
2 key = A[j]
3 i = j - 1
5 while i > 0 and A[i] > key
6 A[i + 1] = A[i]
7 i = i - 1
8 A[i + 1] = key
‘
no final, mas depois do incremento de j.
Exemplo 2
●
A invariante do laço interno é
0≤i 1≤k≤i-1: A[k]≤A[k+1]
∧∀ ∧
key≤A[i+1] i+1≤k≤j-1: A[k]=A[k+1]
∀
INSERTION-SORT(A)
1 for j = 2 to A.length
2 key = A[j]
3 i = j - 1
5 while i > 0 and A[i] > key
6 A[i + 1] = A[i]
7 i = i - 1
8 A[i + 1] = key
‘
depois do decremento de i
Exemplo 2
Prova:
A invariante do laço principal é
“para uma iteração j, o vetor A[1..j-1] está ordenado com j≤n+1.”
Inicialização: Após j=2 na linha 1, a invariante se mantém, pois o único
elemento é A[1] e j=2 implica que 0<j≤n+1, visto que é assumido que n≥1.
Manutenção: Após a linha 1, 0<j≤n. Para o while da variável i, temos a
seguinte invariante de laço:
“0≤i, o vetor A[1..i] está ordenado, key≤A[i+1] e os elementos de A[i+1..j-1]
foram copiados para A[i+2..j].”
Inicialização do laço interno: Após a linha 4, a invariante de laço para i é
verdadeira, pois como i=j-1 e 0<j≤n, então 0≤i. Além disso, o vetor A[1..j-1] já
estava ordenado da invariante de laço principal, logo A[1..i] está ordenado.
Temos que key=A[j] e A[j]≤A[j], então key≤A[j]=A[i+1]. Podemos dizer que
A[i+1..j-1] foi copiado para A[i+2..j], visto que esses vetores são vazios, pois
i+1>j-1.
Exemplo 2
Manutenção do laço interno: Após a linha 5, j≤n, 0<i e A[i]>key implica que
key≤A[i]. Após a linha 6, key≤A[i+1] e A[i]=A[i+1]. Logo, com a invariante do
while com i, temos agora que A[i..j-1] foi copiado para A[i+1..j]. Após a linha 7,
0≤i, key≤A[i+2] e A[i+1..j-1] foi copiado para A[i+2..j]. Como A[i+1..j-1] foi
copiado para A[i+2..j], A[i+1]=A[i+2]. Logo, key≤A[i+1], concluindo a
manuntenção do laço interno.
Finalização do laço interno: Após o laço interno, i≤0 ou A[i]≤key. Para a
primeira alternativa, se i≤0 e a invariante do while para i se mantém, então i=0.
Logo, key≤A[1] e A[1..j-1] é igual a A[2..j]. Para a segunda alternativa, se
A[i]≤key e a invariante do laço interno é verdadeira, A[i]≤key e key≤A[i+1]
implicam que A[i]≤A[i+1]. Com isto e que A[1..i] está ordenado e A[i+1..j-1] foi
copiado para A[i+2..j], temos que A[1..j] está ordenado. Após a linha 8, na
primeira alternativa, key≤A[1] torna-se A[i+1]≤A[1]. Como i=0, esta
desigualdade é sempre verdadeira. Como A[1..j-1] é igual a A[2..j] e key é A[1],
então para a primeira alternativa também resulta em A[1..j] ordenado.
Exemplo 2
Continuando a manuntenção do laço principal, após a linha 9, como A[1..j]
ordenado e j≤n, têm-se que o vetor A[1..j-1] está ordenado com j≤n+1.
Término: Quando o laço principal encerra, j>n. Pela invariante de laço, j>n e
j≤n+1 implicam que j=n+1. Logo, A[1..n] está ordenado.
Exercícios
1) Provar corretude dos algoritmos deste
slide que não possuem pré- e pós-
condições (MINMAX1, MINMAX2, ...).
Observe que em algoritmos recursivos
deve-se utilizar indução. Apresente
também a prova em linguagem natural.
2) Faça o mesmo para os algoritmos a
seguir:
Exercícios
{n>0}
Fib(n)
i, a, b = 1, 1, 0
while i < n
i, a, b = i+1, a+b, a
{a = fn}
return a
{n>0}
maiorPotenciaDe2(n)
i = 1
while 2i < n
i = 2i
{0 < i ≤ n< 2i p : i = 2
∧∃ ∈ℕ p
}
return i
f0 = 0
f1 = 1
fn = fn-1 + fn-2
Exercícios
Exercícios
3) Mostre que o seguinte algoritmo realiza a
multiplicação de dois números binários de
tamanho n. Mostre também que o tempo no
pior caso é O(n1,59
).
Dicas:
– A prova não será por invariante de laço, pois o
algoritmo é recursivo.
– Seja x = 101101102. Então, n = 8, xL = 1011, xR =
0110 e x = 101101102 = 1011∙24
+ 0110 = xL∙2n/2
+xR.
●
Multiplicar por 2i
pode ser feito com i
deslocamentos à esquerda.
Exercícios
GAUSSMULT(x,y)
1 if n = 1 return xy
2 xL, xR = n/2
⌈ ⌉ bits à esquerda de x, n/2
⌊ ⌋
bits a direita de x
3 yL, yR = n/2
⌈ ⌉ bits à esquerda de y, n/2
⌊ ⌋
bits a direita de y
4 P1 = GAUSSMULT(xL,yL)
5 P2 = GAUSSMULT(xR,yR)
6 P3 = GAUSSMULT(xL+xR,yL+yR)
7 return P12n
+ (P3-P1-P2)2n/2
+ P2
Referência
●
Gries, David. The science of programming.
1981. Springer-Verlag.
●
Cormen, T.H., Leiserson, C.E., Rivest, R.L.
Stein, C. Introduction to Algorithms. Third
edition. 2009. MIT Press.

03-Notacoes O omega teta Complexidade e Corretude.pdf

  • 1.
    Notações O, Ωe Θ Yuri Tavares dos Passos
  • 2.
    Notação O, Ωe Θ ● São utilizadas para denotar o comportamento assintótico de uma função
  • 3.
    Notação O, Ωe Θ ● Notação O – É usada para determinar um limite superior assintótico. ● f(n) O(g(n)) → ∈ ● f(n)∈{h(n) | c,N ∃ ∈ℝ: ∀n≥N: h(n) ≤ cg(n)} ● f(n) é limitada superiormente por g(n)
  • 4.
    Notação O, Ωe Θ ● Também usa-se f(x) = O(g(x)) ● Assim, também podemos afirmar que f(x) = O(g(x)) se e somente se a ∃ ∈ℝ, a ≥ 0: lim x→∞ f ( x) g(x) ≥a
  • 5.
    Notação O, Ωe Θ ● Observe que este limite pode ser igual a 0.
  • 6.
    Notação O, Ωe Θ ● Exemplos: – 5n2 +15∈O(n2 ) → n ∀ ≥4: 5n2 +15 ≤ 6n2 (c=6, N=4) – 5n2 +15∈O(n3 ) → n ∀ ≥6: 5n2 +15 ≤ n3 (c=1, N=6) – n2 ∉ O(n) → c,N ∄ ∈ℝ: n ∀ ≥N n2 ≤ cn ● Suponha c,N, tais que n ∃ ∀ ≥N n2 ≤ cn. Então n ∀ ≥N n2 - cn ≤ 0. As soluções para n são 0 ≤ n ≤ c. Como n está limitado entre 0 e c, ele não pode ser maior que qualquer N. Contradição!
  • 7.
  • 8.
    Notação O, Ωe Θ ● Notação Ω – É usada para determinar um limite inferior assintótico. ● f(n) Ω(g(n)) → ∈ ● f(n)∈{h(n) | c,N ∃ ∈ℝ: n ∀ ≥N: h(n) ≥ cg(n)} ● f(n) é limitada inferiormente por g(n)
  • 9.
    Notação O, Ωe Θ ● Também usa-se f(x) = Ω(g(x)) ● Assim, também podemos afirmar que f(x) = Ω(g(x)) se e somente se a ∃ ∈ℝ, 0 < a ≤ ∞: lim x→∞ f ( x) g(x) =a
  • 10.
    Notação O, Ωe Θ ● Observe que este limite não pode ser igual a 0.
  • 11.
    Notação O, Ωe Θ ● Exemplos: – 5n2 +15∈Ω(n) → n ∀ ≥4: 5n2 +15 ≥ n (c=1, N=4) – 5n2 +15∉Ω(n3 ) → c,N ∄ ∈ℝ: n ∀ ≥N 5n2 +15 ≥ cn3
  • 12.
  • 13.
    Notação O, Ωe Θ ● Notação Θ – É usada para determinar uma igualdade assintótica. ● f(n)∈Θ(g(n)) s.s.s. f(n)∈O(g(n)) e f(n)∈Ω(g(n)) ● f(n) é assintoticamente equivalente a g(n)
  • 14.
    Notação O, Ωe Θ ● Também usa-se g(n) = Θ(f(n)) ● Assim, também podemos afirmar que f(x) = Ω(g(x)) se e somente se a ∃ ∈ℝ, 0 < a < ∞: lim x→∞ f ( x) g(x) =a
  • 15.
    Notação O, Ωe Θ ● Exemplos: – 5n2 +15∈Θ(n2 ) – 4n+1∈Θ(n)
  • 16.
  • 17.
    Propriedades ● Seja k ep constantes e n variável. – k O(np ) = O(np ) – n O(nk ) = O(nk+1 ) – O(nk ) + O(np ) = O(nmax(k,p) ) ● Ex.: O(n) + O(n4 ) = O(n4 ) – O(f(n)) + O(g(n)) = O(max(f(n),g(n))) ● Ex.: O(log(n)) + O(n) = O(n) ● Similar para Θ. ● Para Ω troca-se max por min.
  • 18.
    Resumo ● O “equivale ao”≤. ● Ω “equivale ao” ≥. ● Θ “equivale ao” =.
  • 19.
    Referência ● HOPCROFT, John E.;ULLMAN, Jeffrey D.; MOTWANI, Rajeev. Introdução à teoria de autômatos, linguagens e computação. [Rio de Janeiro]: Campus, 2003. ● Imagens da versão em inglês
  • 20.
    Introdução a análisede algoritmos Yuri Tavares dos Passos
  • 21.
    Motivação ● Computadores usam recursoscomo tempo e espaço. ● Diferentes algoritmos podem resolver um mesmo problema, mas é preferível aquele que use menos recursos. ● A análise deve ser independente da máquina, pois operações podem levar tempos diferentes em diferentes máquinas. – CISC: uma soma com três operandos pode custar apenas um ciclo. – RISC: precisa-se mover os valores da memória para registrador antes de somar.
  • 22.
    Tipos de análise ● Melhorcaso – Considera apenas o caso com menor custo. ● Pior caso – Similar, mas para o maior custo. ● Caso médio – Considera a média de todos os casos considerando a probabilidade de cada um deles ocorrer. E(X)=∑ i=1 n P( X=xi) xi
  • 23.
    Exemplo 1 SWAP(x,y) 1 t= x 2 x = y 3 y = t
  • 24.
    Exemplo 1 SWAP(x,y) 1 t= x 2 x = y 3 y = t custo c1 c2 c3 vezes 1 1 1 ci é constante (possivelmente diferente para cada i).
  • 25.
    Exemplo 1 ● As constantespoderiam ser diferentes a depender da máquina ou implementação. – Se x, y e t são registradores, c1 = c2 = c3 = 1. – Caso x, y e t estejam na memória RAM, c1 = c2 = c3 = 2. ● Custo total é c1 + c2 + c3 = Θ(1).
  • 26.
    Exemplo 2 MIN(A) 1 min= A[1] 2 for i = 2 to A.length 3 if min > A[i] 4 min = A[i] 5 return min ● Seja n o tamanho do vetor A.
  • 27.
    Exemplo 2 MIN(A) 1 min= A[1] 2 for i = 2 to A.length 3 if min > A[i] 4 min = A[i] 5 return min custo c1 c2 c3 c4 c5 vezes 1 n n-1 tA 1 tA é o número de vezes que a linha 4 é executada para um dado vetor A.
  • 28.
    Exemplo 2 ● Os testesdos comandos for ou while rodam uma vez a mais antes de encerrar. ● O custo total é c1 + c2n + c3(n-1) + c4tA + c5 ● O pior caso ocorre quando tA = n-1. – Ex.: A = [4 3 2 1] ● O custo de pior caso é ● c1 + c2n + c3(n-1) + c4(n-1) + c5 = Θ(n)
  • 29.
    Exemplo 2 ● O melhorcaso ocorre quando tA = 0. – Ex.: A = [1 2 3 4] ● O custo de melhor caso é c1 + c2n + c3(n-1) + c5 = Θ(n) ● O custo de caso médio é obtido pelo valor esperado de tA, E[tA].
  • 30.
    Exemplo 2 ● Considere Xio evento que é 1 caso na iteração i do algoritmo, o valor de min seja modificado: Xi= {1,se min> A[i], 0, se min≤A[i].
  • 31.
    Exemplo 2 A X2X3 X4 tA 1 2 3 4 0 0 0 0 1 2 4 3 0 0 0 0 1 3 2 4 0 0 0 0 1 3 4 2 0 0 0 0 1 4 2 3 0 0 0 0 1 4 3 2 0 0 0 0 2 1 3 4 1 0 0 1 2 1 4 3 1 0 0 1 2 3 1 4 0 1 0 1 2 3 4 1 0 0 1 1 2 4 3 1 0 0 1 1 2 4 1 3 0 1 0 1 A X2 X3 X4 tA 3 1 2 4 1 0 0 1 3 1 4 2 1 0 0 1 3 2 1 4 1 1 0 2 3 2 4 1 1 0 1 2 3 4 1 2 0 1 0 1 3 4 2 1 0 1 1 2 4 1 2 3 1 0 0 1 4 1 3 2 1 0 0 1 4 2 1 3 1 1 0 2 4 2 3 1 1 0 1 2 4 3 1 2 1 1 0 2 4 3 2 1 1 1 1 3
  • 32.
    Exemplo 2 ● Temos que E[tA]= E[X2] + E[X3] + … + E[Xn] ... E[X2] = 0·½ + 1·½ = ½ 1 2 n E[X3] = 0·(2/3) + 1·⅓ = ⅓ 1 2 3 n 1 2 i n E[Xi] = 1/i
  • 33.
    Exemplo 2 E[tA] =E[X2] + E[X3] + … + E[Xn] = 1/2 + 1/3 + … + 1/n = Hn – 1 = Θ(ln n) 0 1 2 3 4 5 0 10 20 30 40 50 H n n
  • 34.
    Exemplo 2 ● Custo decaso médio é c1 + n c2 + (n-1) c3 + (Hn - 1) c4 + c5 = c1 + n c2 + (n-1) c3 + Θ(ln n) c4 + c5 = Θ(n)
  • 35.
    Exemplo 3 ● Seja no tamanho do vetor A.
  • 36.
    tj é onúmero de vezes que o teste do while na linha 5 é executado para um dado valor j do for na linha 1.
  • 37.
    Exemplo 3 ● O custototal é c1 n+c2(n−1)+c4(n−1)+c5∑ j=2 n t j+c6 ∑ j=2 n (t j−1) +c7∑ j=2 n (t j−1)+c8(n−1) ● Melhor caso para tj = 1 com A ordenado: c1 n+c2(n−1)+c4 (n−1)+c5(n−1)+c8(n−1)=Θ(n)
  • 38.
    Exemplo 3 ● O piorcaso com tj = j para A em ordem decrescente: c1 n+c2(n−1)+c4(n−1)+c5 ∑ j=2 n j+c6 ∑ j=2 n ( j−1) +c7 ∑ j=2 n ( j−1)+c8(n−1)= c1 n+c2(n−1)+c4(n−1)+c5(n(n+1) 2 −1)+c6(n(n−1) 2 ) +c7(n(n−1) 2 )+c8(n−1)=Θ(n2 )
  • 39.
    Exemplo 3 ● O casomédio utiliza-se E[tj]. Para cada j, tj pode ser um valor de 1 a j, com igual probabilidade, 1/j. 1 2 j-1 j ... key E[t j]=1 1 j +2 1 j +...+ j 1 j =∑ t=1 j 1 j t= j+1 2
  • 40.
    Exemplo 3 ● Usando E[tj]no custo total, temos: c1 n+c2(n−1)+c4(n−1)+c5∑ j=2 n E[t j ]+c6 ∑ j=2 n (E[t j]−1) +c7 ∑ j=2 n (E[t j ]−1)+c8(n−1)= c1 n+c2(n−1)+c4(n−1)+c5(n(n−1) 4 +n−1)+c6(n(n−1) 4 ) +c7(n(n−1) 4 )+c8(n−1)=Θ(n 2 )
  • 41.
    Análise simplificada ● Melhor epior casos podem ser analisados de forma simplificada em alguns casos como no exemplo a seguir.
  • 42.
    Exemplo 1 (melhorcaso) MIN(A) 1 min = A[1] 2 for i = 2 to A.length 3 if min > A[i] 4 min = A[i] 5 return min tempo Θ(1) Θ(n) Θ(n) 0 Θ(1) Total é 2Θ(n)+2Θ(1) = Θ(n)
  • 43.
    Exemplo 1 (piorcaso) MIN(A) 1 min = A[1] 2 for i = 2 to A.length 3 if min > A[i] 4 min = A[i] 5 return min tempo Θ(1) Θ(n) Θ(n) Θ(n) Θ(1) Total é 3Θ(n)+2Θ(1) = Θ(n)
  • 44.
    Exemplo 2 (melhorcaso) tempo Θ(n) Θ(n) 0 Θ(n) Θ(n) 0 0 Θ(n) Total é 5Θ(n) = Θ(n)
  • 45.
    Exemplo 2 (piorcaso) tempo Θ(n) Θ(n) 0 Θ(n) Θ(n2 ) Θ(n2 ) Θ(n2 ) Θ(n) Total é 3Θ(n2 )+4Θ(n) = Θ(n2 )
  • 46.
    Custos escondidos ● As notaçõesO, Ω e Θ podem esconder custo úteis para comparação entre algortimos de mesma ordem de complexidade. ● No exemplo a seguir, ambos os algoritmos são Θ(n), mas um é Θ(2n) enquanto que o outro é Θ(1,5n) (ou seja, mais eficiente).
  • 47.
    Exemplo MINMAX1(A) 1 min =max = A[1] 2 for i = 2 to A.length 3 if min > A[i] 4 min = A[i] 5 else if max < A[i] 6 max = A[i] 7 return (min, max)
  • 48.
    MINMAX2(A) 1 if A.lengthmod 2 ≠ 0 2 min = max = A[1] 3 i = 2 4 else 5 if A[1] < A[2] 6 min = A[1] 7 max = A[2] 8 else 9 min = A[2] 10 max = A[1] 11 i = 3 12 while i ≤ A.length 13 if A[i] < A[i+1] 14 if A[i] < min 15 min = A[i] 16 if A[i+1] > max 17 max = A[i+1] 18 else 19 if A[i+1] < min 20 min = A[i] 21 if A[i] > max 22 max = A[i+1] 23 i = i + 2 24 return (min, max)
  • 49.
    Exercício ● Calcule o custode tempo total de execução dos algoritmos MINMAX1 e MINMAX2. Qual é o custo no melhor e no pior caso?
  • 50.
    Complexidade de espaço ● Ésimilar a de tempo, mas as colunas custo e vezes são substituídas pelo consumo de memória. ● Criação de novas variáveis tem custo constante, mas utilização tem custo zero.
  • 51.
    Exemplo MIN(A) 1 min =A[1] 2 for i = 2 to A.length 3 if min > A[i] 4 min = A[i] 5 return min custo c1 c2 0 0 0 vezes 1 1 0 0 0
  • 52.
    Exemplo 1 ● Custo deespaço é c1+c2=Θ(1). ● Se min e i são inteiros de 8 bit, então o custo de espaço da função MIN é de apenas 16 bits. ● Observe que foi assumido que A foi passado previamente sem necessitar armazenar novamente.
  • 53.
  • 54.
  • 55.
    Exemplo 2 ● Custo deespaço é c1+c2+c3(n1+n2+2)+c4+c5+c6 = c1+c2+c3(q-p+1+r-q+2)+c4+c5+c6 = c1+c2+c3(r-p+3)+c4+c5+c6 = Θ(r-p+3).
  • 56.
    Referência ● Cormen, T.H., Leiserson,C.E., Rivest, R.L. Stein, C. Introduction to Algorithms. Third edition. 2009. MIT Press.
  • 57.
  • 58.
    Teorema mestre ● Útil paracalcular complexidade de algoritmos recursivos. ● Exemplos: quicksort e mergesort.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
    Mergesort ● tL é aquantidade de vezes que um elemento de L foi selecionado. ● Custo total do MERGE é 7Θ(1) + Θ(n1+1) + Θ(n1) + Θ(n2+1) + Θ(n2) + Θ(r-p+2) + Θ(r-p+1) + 2Θ(tL) + 2Θ(r-p+1-tL) = Θ(1) + Θ(n1) + Θ(n2) + Θ(r-p+2) + Θ(r-p+1) + 2Θ(r-p+1) = Θ(1)+Θ(r-p+1)+Θ(r-p+1) = Θ(r-p+1), visto que n1 + n2 = q – p + 1 + r – q = r – p + 1.
  • 65.
  • 66.
    Mergesort ● Se p ≥r, temos o caso base T(i) = Θ(1) para qualquer i ≤ 1. ● Tipicamente, p = 1 e r = n = |A|, assim: T(n) = T(r-p+1) T(n) = 2Θ(1) + T(q-p+1) + T(r-(q+1)+1) + Θ(n) T(n) = T( (n+1)/2 )+T(n- ⌊ ⌋ (n+1)/2 )+Θ(n)+Θ(1) ⌊ ⌋ T(n) = T( n/2 )+T( ⌈ ⌉ n/2 )+Θ(n), ⌊ ⌋ ● Abusando a notação, para simplificar: T(n) = 2 T(n/2) + Θ(n), se n > 1.
  • 67.
    Teorema mestre Seja a≥ 1 e b > 1, f(n) uma função e T(n) definido sobre inteiros não negativos pela equação recursiva T(1) = Θ(1) e T(n) = aT(n/b) + f(n) se n > 1. onde n/b pode ser interpretado como n/b ⌊ ⌋ ou n/b . Então, ⌈ ⌉
  • 68.
    Teorema mestre 1) Sef(n) = O(nlog b a-ε ) para ε > 0, então T(n) = Θ(nlog b a ). 2) Se f(n) = Θ(nlog b a ), então T(n) = Θ(nlog b a log2n). 3) Se f(n) = Ω(nlog b a+ε ) para ε > 0 e se af(n/b) ≤ cf(n) para alguma constante c < 1 e n suficientemente grande, T(n) = Θ(f(n)).
  • 69.
    Interpretação ● Comparamos f(n) comnlog b a . – A maior delas determina a solução da recursão. ● Se elas são “do mesmo tamanho”, adicione um fator logaritmo na solução.
  • 70.
    Interpretação ● Maior (menor) nosentido polinomial: maior (menor) que nlog b a por um fator nε . – Existem possibilidades entre os casos 1 e 2 e os casos 2 e 3 que não são tratados no teorema e não podem ser aplicados.
  • 71.
    Exemplo 1 T(n) =9T(n/3) + n. ● a = 9, b = 3, f(n) = n, nlog b a = nlog 3 9 = n2 . ● f(n) = n = O(n2-1 ) = O(nlog 3 9-1 ) = O(nlog a b-ε ) com ε = 1. ● Pelo caso 1: T(n) = Θ(nlog b a ) = Θ(n2 ).
  • 72.
    Exemplo 2 T(n) =T(2n/3) + 1. ● a = 1, b = 3/2, f(n) = 1, nlog b a = nlog (3/2) 1 = n0 =1. ● f(n) = 1 = Θ(1) = Θ(n0 ) = Θ(nlog (3/2) 1 ). ● Pelo caso 2: T(n) = Θ(nlog b a log2n) = Θ(log2n).
  • 73.
    Exemplo 3 T(n) =3T(n/4) + n log2n. ● a = 3, b = 4, f(n) = n log2n, nlog b a = nlog 4 3 = O(n0,793 ). ● f(n) = n log2n = Ω(n) e n = Ω(n0,993 ) = Ω(n0,793+0.2 )=Ω(nlog 4 3+0.2 )=Ω(nlog a b+ε ) com ε=0,2. ● Para n grande, a f(n/b) = 3(n/4)log2(n/4) ≤ (3/4)n log2n = c f(n) para c = 3/4. ● Pelo caso 3: T(n) = Θ(f(n)) = Θ(n log2n).
  • 74.
    Exemplo 4 T(n) =2T(n/2) + n log2n. ● a = 2, b = 2, f(n) = n log2n, nlog b a = nlog 2 2 = n. ● f(n) = n log2n = Ω(n) porém não existe ε > 0 tal que f(n) = n log2n = Ω(nlog b a+ε ) = Ω(n1+ε ) ● pois log2n = O(nε ) para qualquer ε > 0. ● Este exemplo está entre os casos 2 e 3.
  • 75.
    De onde vemo nlog b a ?
  • 76.
    Exercícios 1) Use oteorema mestre para obter o custo de tempo e espaço do mergesort. 2) Calcule a complexidade de tempo e espaço do quicksort no melhor e pior caso.
  • 77.
  • 78.
    Referência ● Cormen, T.H., Leiserson,C.E., Rivest, R.L. Stein, C. Introduction to Algorithms. Third edition. 2009. MIT Press.
  • 79.
    Corretude de algoritmos YuriTavares dos Passos
  • 80.
    Motivação ● Verificação automática decódigo por asserções (comando assert de algumas linguagens). ● Exemplo:
  • 81.
    Pré-condição e pós- condição ● Sãoproposições lógicas que devem ser satisfeitas antes e depois da execução de um comando. ● Notação: {P} Pré-condição S Comando {Q} Pós-condição
  • 82.
    Exemplos {0≤a∧0≤b} z = a*b {z= ab} {x = X y = Y ∧ } SWAP(x,y) {x = Y y = X ∧ } {V} i = 1 {i = 1} Verdadeiro i = 1 {i = 1} Omissão significa hipótese verdadeira
  • 83.
    Provas usando pré-e pós-condições ● Assuma que P P ⇒ 1 e Q Q ⇒ 1. Então também temos: Justificativa {P} {P1} [P P ⇒ 1] S {Q} {Q1} [Q Q ⇒ 1]
  • 84.
    Exemplo ● Provar que SWAPtroca os elementos. {x = X y = Y ∧ } SWAP(x,y) 1 t = x 2 x = y 3 y = t {x = Y y = X ∧ }
  • 85.
    Exemplo {x = Xy = Y ∧ } SWAP(x,y) {x = X y = Y ∧ } 1 t = x {t = X∧x = X y = Y ∧ } 2 x = y {t = X∧x = Y y = Y ∧ } 3 y = t {t = X∧x = Y y = X ∧ } {x = Y y = X} [P Q Q ∧ ∧ ⇒ ]
  • 86.
    Atribuições {P} i = 1 {i= 1 P ∧ } ● A atribuição de uma constante a uma variável adiciona uma igualdade a pré- condição na pós-condição:
  • 87.
    Atribuições {i=4} {i+1=5} i = i+1 {i=5} ● Atribuiçõesde expressões devem substituir a expressão pela variável na pós-condição. {i≥0} {i+1≥1} i = i+1 {i≥1} {i>0}
  • 88.
    Atribuições {i≥0 s=1+2+...+i ∧ } {i+1>0s=1+2+...+ ∧ i+1-1} [álgebra] i = i+1 {i>0 s=1+2+...+ ∧ i-1} {i>0 s+i=1+2+...+i ∧ } [+i ambos lados] s = s+i {i>0 s=1+2+...+i ∧ }
  • 89.
    Seleção ● Nos comandos if… else, a condição do if é adicionada com e lógico a pré-condição na pós-condição dentro do if. No else, a negação da condição é adicionada dentro do else. {P} if cond {cond P ∧ } S1 {cond P Q ∧ ∧ } else {¬cond P ∧ } S2 {¬cond P Q ∧ ∧ }
  • 90.
    Seleção ● Se o comandoif não possuir um else associado, deve-se analisar como se existisse um else seguido de nenhum comando (ou skip). {P} if cond {cond P ∧ } S1 {cond P Q ∧ ∧ } //else // skip {¬cond P ∧ }
  • 91.
    Exemplo ● Provar que oalgoritmo a seguir calcula |x|. {x=X} if x<0 x=-x {x=|X|} |x|={ x ,x≥0 −x , x<0
  • 92.
    Exemplo ● Assuma que háum else skip. {x=X} if x<0 x=-x else skip {x=|X|} |x|={ x ,x≥0 −x , x<0
  • 93.
    Exemplo {x=X} if x<0 {x=X x<0} ∧ {-x=-XX<0} [(-1)x=(-1)X e x=X em x<0] ∧ x=-x {x=-X X<0} ∧ {x=|X|} [def. |X|] else {x=X x ∧ ≥0} skip {x=X x ∧ ≥0} {x=X X ∧ ≥0} [x=X em x≥0] {x=|X|} [def. |X|] {x=|X|}
  • 94.
    Laços ● Para o comandowhile, a condição do comando é adicionada a pós-condição depois da condição. No fim do while, a negação da condição é adicionada a pós- condição do último commando no while. ● Para comando for é similar: for i = s to t S i = s while (i≤t) S i = i + 1 ⇔
  • 95.
    Exemplo 1 ● Mostre queo seguinte algoritmo soma todos os elementos de um vetor de tamanho n≥0 antes de retornar. {n≥0} SUM(A) 1 i = 1 2 s = 0 3 while i ≤ n 4 s = s+A[i] 5 i = i+1 6 return s {s=∑ k=1 n A[k] }
  • 96.
    Exemplo 1 {n≥0} SUM(A) {n≥0} 1 i= 1 2 s = 0 {n+1≥1 i=1 s = 0} [+1 em n ∧ ∧ ≥0] [i em n+1≥1, def. ∑] 3 while i ≤ n {i≤n∧i≤n+1∧ s=∑ k=1 i−1 A[k]} {i≤n+1∧ s=∑ k=1 i−1 A[k]}
  • 97.
    Exemplo 1 {i≤n+1∧ s=∑ k=1 i−1 A[k]} [SIMP] ∧ 4 s = s+A[i] 5 i = i+1 // fim do while {i+1≤n+1∧s+A[i]=∑ k=1 i A[k]} {i>n∧i≤n+1∧s=∑ k=1 i−1 A[k]} {i≤n∧ s=∑ k=1 i−1 A[k]} {i=n+1∧ s=∑ k=1 i−1 A[k]} 6 return s {s=∑ k=1 n A[k]}
  • 98.
    Exemplo 2 ● Mostre queo seguinte algoritmo calcula o menor elemento num vetor de tamanho n≥1 antes de retornar. {n≥1} MIN(A) 1 min = A[1] 2 for i = 2 to n 3 if min > A[i] 4 min = A[i] { k : 0<k ∀ ∈ℕ ≤n⇒min≤A[k]} 5 return min
  • 99.
    Exemplo 2 MIN(A) 1 min= A[1] 2 for i = 2 to n 3 if min > A[i] 4 min = A[i] 5 return min MIN(A) 1 min = A[1] 2 i = 2 2 while i ≤ n 3 if min > A[i] 4 min = A[i] 5 i = i + 1 6 return min
  • 100.
    Exemplo 2 {n≥1} MIN(A) {n≥1} {n+1≥2} 1 min= A[1] 2 i = 2 {n+1≥2∧i = 2 min = A[1]} ∧ {i≤n+1 i=2 k : 0<k ∧ ∧∀ ∈ℕ ≤1 min ⇒ ≤A[k]} {i≤n+1∧ k : 0<k ∀ ∈ℕ ≤i-1 min ⇒ ≤A[k]} 2 while i ≤ n {i≤n i ∧ ≤n+1∧∀k : 0<k ∈ℕ ≤i-1 min ⇒ ≤A[k]} {i≤n k : 0<k ∧∀ ∈ℕ ≤i-1 min ⇒ ≤A[k]} 3 if min > A[i]
  • 101.
    Exemplo 2 3 ifmin > A[i] {min>A[i] i ∧ ≤n∧∀k : 0<k ∈ℕ ≤i-1 min ⇒ ≤A[k]} {i≤n∧ k : 0<k ∀ ∈ℕ ≤i-1 A[i]<min ⇒ ≤A[k]} {i≤n∧ k : 0<k ∀ ∈ℕ ≤i-1 A[i] ⇒ ≤A[k]} {i≤n A[i] ∧ ≤A[i] k : 0<k ∧∀ ∈ℕ ≤i-1 A[i] ⇒ ≤A[k]} [A[i]≤A[i]] {i≤n k : 0<k ∧∀ ∈ℕ ≤i A[i] ⇒ ≤A[k]} 4 min = A[i] {i≤n k : 0<k ∧∀ ∈ℕ ≤i min ⇒ ≤A[k]} //else skip {min≤A[i] i ∧ ≤n∧ k : 0<k ∀ ∈ℕ ≤i-1 min ⇒ ≤A[k]} {i≤n k : 0<k ∧∀ ∈ℕ ≤i min ⇒ ≤A[k]} {i+1≤n+1 k : 0<k ∧∀ ∈ℕ ≤i+1-1 min ⇒ ≤A[k]} 5 i = i + 1 {i≤n+1 k : 0<k ∧∀ ∈ℕ ≤i-1 min ⇒ ≤A[k]} //fim do while {i>n i ∧ ≤n+1 k : 0<k ∧∀ ∈ℕ ≤i-1 min ⇒ ≤A[k]}
  • 102.
    Exemplo 2 {i>n i ∧≤n+1 k : 0<k ∧∀ ∈ℕ ≤i-1 min ⇒ ≤A[k]} {i=n+1 k : 0<k ∧∀ ∈ℕ ≤i-1 min ⇒ ≤A[k]} { k : 0<k ∀ ∈ℕ ≤n min ⇒ ≤A[k]} 6 return min
  • 103.
    Invariante de laço ● Éuma condição que deve existir antes de um laço começar e no fim dele, antes de retornar ao teste do laço. ● O objetivo dele é se tornar o resultado desejado quando alcançar o final do algoritmo. ● A invariante de laço também deve ser verdadeira após os comandos de inicializaçao antes do laço.
  • 104.
    Exemplo 1 SUM(A) 1 i= 1 2 s = 0 3 while i ≤ n 4 s = s+A[i] 5 i = i+1 {i≤n+1∧ s=∑ k=1 i−1 A[k]} {i≤n+1∧ s=∑ k=1 i−1 A[k]} 6 return s {i>n∧i≤n+1∧s=∑ k=1 i−1 A[k]} {s=∑ k=1 n A[k]}
  • 105.
    Exemplo 2 MIN(A) 1 min= A[1] 2 for i = 2 {i≤n+1∧ 0<k ∀ ≤i-1: min≤A[k]} to n 3 if min > A[i] 4 min = A[i] // depois de i = i + 1 {i≤n+1 0<k ∧∀ ≤i-1: min≤A[k]} //fim do for {i>n i ∧ ≤n+1 0<k ∧∀ ≤i-1: min≤A[k]} { 0<k ∀ ≤n: min≤A[k]} 5 return min
  • 106.
    Exemplo 3 MULTIPLICAR(a,b) 1 x,y, z = a, b, 0 2 while y > 0 3 if y mod 2 = 0 4 y, x = y div 2, x+x 5 else 6 y, z = y-1, z+x 7 return z
  • 107.
    Exemplo 3 {0≤b} MULTIPLICAR(a,b) 1 x,y, z = a, b, 0 {0≤y z+xy = ab ∧ } 2 while y > 0 3 if y mod 2 = 0 4 y, x = y div 2, x+x {0≤y z+xy = ab ∧ } 5 else 6 y, z = y-1, z+x {0≤y z+xy = ab ∧ } // fim do while {y=0 0 ∧ ≤y z+xy = ab} ∧ {z = ab} 7 return z
  • 108.
    Exercício ● Apresente as demaispré- e pós condições ao longo do algoritmo anterior de forma a provar as invariantes de laço após as linhas 1, 4, 6 e 7.
  • 109.
    Prova de corretudeem linguagem natural ● A prova de corretude em linguagem natural é escrita em cima das pré- e pós- condições verificadas pelos comandos do algoritmo junto com a invariante de laço. ● Deve-se enunciar a invariante de laço e provar as seguintes três partes:
  • 110.
    Prova de corretudeem linguagem natural ● Inicialização: consiste em demostrar que a invariante de laço é verdadeira antes de iniciar o laço e após os comandos anteriores ao laço. ● Manuntenção: mostra que a invariante se mantém no início e no fim do laço antes de retornar a condição de teste do laço. ● Término: prova que a invariante junto com a negação da condição do laço estabelece o objetivo final do algoritmo.
  • 111.
    Exemplo 1 {n≥0} SUM(A) 1 i= 1 2 s = 0 3 while i ≤ n 4 s = s+A[i] 5 i = i+1 6 return s {i≤n+1∧s=∑ k=1 i−1 A[k ]} {i≤n+1∧s=∑ k=1 i−1 A[k ]} {i>n∧i≤n+1∧s=∑ k=1 i−1 A[k ]} {s=∑ k=1 n A[k]} Prova: A invariante de laço é a seguinte: “para cada laço, indexado por i, i ≤ n+1 e .” Inicialização: Antes da linha 3, e i ≤ n+1, pois i = 1 e n ≥ 0⇔n+1 ≥ 1 = i. Manuntenção: Após a linha 4, temos Depois da linha 5, i torna-se i+1, assim s passa a ser e i ≤ n+1, mantendo a invariante de laço no fim do laço. Término: Após o comando enquanto, antes da linha 6, i > n e i ≤ n+1. Logo, i = n+1 e s=∑ k=1 i−1 A[k] s=0=∑ k=1 0 A[k]=∑ k=1 i−1 A[k] s=∑ k=1 i A[k]. ∑ k=1 i−1 A[k] s=∑ k=1 n A[k].
  • 112.
    Exemplo 2 MIN(A) 1 min= A[1] 2 for i = 2 to n 3 if min > A[i] 4 min = A[i] 5 return min MIN(A) 1 min = A[1] 2 i = 2 {invariante de laço} 3 while i ≤ n 4 if min > A[i] 5 min = A[i] 6 i = i + 1 {invariante de laço} 7 return min
  • 113.
    Exemplo 2 ● A invariantede laço é i≤n+1 k : 0<k≤i-1 min≤A[k] ∧ ∀ ∈ ⇒ ‘ no final, mas depois do incremento de i. MIN(A) 1 min = A[1] 2 for i = 2 to n 3 if min > A[i] 4 min = A[i] 5 return min
  • 114.
    Exemplo 2 {n≥1} MIN(A) 1 min= A[1] 2 for i = 2 {i≤n+1∧ k : 0<k ∀ ∈ℕ ≤i-1 min ⇒ ≤A[k]} to n 3 if min > A[i] 4 min = A[i] {i≤n+1 k : 0<k ∧ ∀ ∈ℕ ≤i-1 min ⇒ ≤A[k]} // depois de i = i + 1 {i≤n+1 k : 0<k ∧ ∀ ∈ℕ ≤i-1 min ⇒ ≤A[k]} //fim do for {i>n i ∧≤n+1 k : 0<k ∧ ∀ ∈ℕ ≤i-1 min ⇒ ≤A[k]} { k : 0<k≤n min≤A[k]} ∀ ∈ℕ ⇒ 5 return min Prova: A invariante de laço é para cada iteração de número i, i ≤ n+1 e, para todo k natural com 0<k≤i-1, min≤A[k]. Inicialização: Depois de inicializar i com 2 na primeira iteração do for na linha 2, como n≥1, então i≤n+1. Como min=A[1] da linha 1, então para todo k satisfazendo 0<k≤i-1=1, min≤A[k]. Manuntenção: Após o if na linha 3, min>A[i]. Assim, pela invariante de laço, para todo k natural com 0<k≤i-1, A[i]<min≤A[k]. Como A[i]≤A[i], têm-se, portanto, que para todo k natural com 0<k≤i, A[i]≤A[k]. Após a linha 4, min=A[i]. Então, para todo k natural com 0<k≤i, min≤A[k] após a linha 4.
  • 115.
    Exemplo 2 Caso acondição no if da linha 4 não seja verdade, então min≤A[i]. Logo, para todo k natural com 0<k≤i, min≤A[k] antes da linha 5. Após a linha 5, a invariante se mantém, visto que i = i+1-1 e substituindo nesta expressão i+1 por i, têm-se que para todo k natural com 0<k≤i-1, min≤A[k]. Término: ao final do comando while, antes da linha 6, têm-se que i>n. Como a invariante afirma que i≤n+1, então i = n+1. Assim, como a invariante também afirma que para todo k natural com 0<k≤i-1, min≤A[k], portanto, para todo k natural com 0<k≤n, min≤A[k]. {n≥1} MIN(A) 1 min = A[1] 2 for i = 2 {i≤n+1∧ k : 0<k ∀ ∈ℕ ≤i-1 min ⇒ ≤A[k]} to n 3 if min > A[i] 4 min = A[i] {i≤n+1 k : 0<k ∧ ∀ ∈ℕ ≤i-1 min ⇒ ≤A[k]} // depois de i = i + 1 {i≤n+1 k : 0<k ∧ ∀ ∈ℕ ≤i-1 min ⇒ ≤A[k]} //fim do for {i>n i ∧≤n+1 k : 0<k ∧ ∀ ∈ℕ ≤i-1 min ⇒ ≤A[k]} { k : 0<k≤n min≤A[k]} ∀ ∈ℕ ⇒ 5 return min
  • 116.
    Exercício {0≤b} MULTIPLICAR(a,b) 1 x, y,z = a, b, 0 {0≤y z+xy = ab ∧ } 2 while y > 0 3 if y mod 2 = 0 4 y, x = y div 2, x+x {0≤y z+xy = ab ∧ } 5 else 6 y, z = y-1, z+x {0≤y z+xy = ab ∧ } {y=0 0 ∧ ≤y z+xy = ab} ∧ {z = ab} 7 return z Faça a prova em linguagem natural.
  • 117.
    Laços aninhados ● Haverá invariantesde laço para cada laço, desde o mais externo até o mais interno.
  • 118.
    Exemplo 1 ● Prove queo algoritmo a seguir soma duas matrizes A e B com resultado na matriz C. {0≤n 0≤m ∧ } 1 for i=1 to n 2 for j=1 to m 3 C[i,j] = A[i,j] + B[i,j] { k,l : 1≤k≤n 1≤l≤m C[k,l]=A[k,l]+B[k,l] ∀ ∈ℕ ∧ ⇒ }
  • 119.
    Exemplo 1 1 i= 1 2 while i≤n 3 j = 1 4 while j≤m 5 C[i,j] = A[i,j] + B[i,j] 6 j = j+1 7 i = i + 1 1 for i=1 to n 2 for j=1 to m 3 C[i,j] = A[i,j] + B[i,j]
  • 120.
    Exemplo 1 {0≤n 0≤m} ∧ 1i = 1 {0≤n 0≤m i=1} ∧ ∧ {i≤n+1 0≤m} [0≤n 1≤n+1, i=1≤n+1] ∧ ⇒ {i≤n+1 0≤m ∧ ∧ ∀k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]} ∈ℕ ∧ ⇒ [Verdadeiro por vacuidade] 2 while i≤n {i≤n i≤n+1 0≤m ∧ ∧ ∧ ∀k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]} ∈ℕ ∧ ⇒ {i≤n 0≤m ∧ ∧ k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]} ∀ ∈ℕ ∧ ⇒ [SIMP ] ∧ 3 j = 1 {j=1 i ∧ ≤n 0≤m ∧ ∧ k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l] ∀ ∈ℕ ∧ ⇒ +B[k,l]}
  • 121.
    Exemplo 1 {j=1 i ∧≤n 0≤m ∧ ∧ k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]} ∀ ∈ ∧ ⇒ {j=1 i ∧ ≤n 0≤m k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l] ∧ ∧∀ ∈ ∧ ⇒ ℕ ∧ ∀l : 1≤l≤0 C[i,l]=A[i,l]+B[i,l]} [Verdadeiro por vacuidade] ∈ℕ ⇒ {i≤n j≤m+1 k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l] ∧ ∧∀ ∈ ∧ ⇒ ℕ ∧ ∀l : 1≤l≤j-1 C[i,l]=A[i,l]+B[i,l]} ∈ℕ ⇒ [0≤m 1≤m+1, j=1≤m+1,j-1=0 ⇒ ] 4 while j≤m {j≤m i ∧ ≤n j≤m+1 k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l] ∧ ∧∀ ∈ ∧ ⇒ ∧ ℕ ∀l : 1≤l≤j-1 C[i,l]=A[i,l]+B[i,l]} ∈ℕ ⇒ {j≤m i ∧ ≤n k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l] ∧∀ ∈ ∧ ⇒ ∧ ℕ ∀l : 1≤l≤j-1 C[i,l]=A[i,l]+B[i,l]} [SIMP ] ∈ℕ ⇒ ∧ 5 C[i,j] = A[i,j] + B[i,j] {j≤m i ∧ ≤n k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l] ∧∀ ∈ ∧ ⇒ ∧ ℕ ∀l : 1≤l≤j-1 C[i,l]=A[i,l]+B[i,l] C[i,j] = A[i,j] + B[i,j]} ∈ℕ ⇒ ∧ {j≤m i ∧ ≤n k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l] ∧∀ ∈ ∧ ⇒ ∧ ℕ ∀l : 1≤l≤j C[i,l]=A[i,l]+B[i,l]} [incluído no último , de 1 até j] ∈ℕ ⇒ ∀ 6 j = j+1
  • 122.
    Exemplo 1 {j≤m i ∧≤n k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l] ∧∀ ∈ ∧ ⇒ ∧ ℕ ∀l : 1≤l≤j C[i,l]=A[i,l]+B[i,l]} [incluído no último , de 1 até j] ∈ℕ ⇒ ∀ 6 j = j+1 {j≤m+1 i ∧ ≤n k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l] ∧∀ ∈ ∧ ⇒ ∧ ℕ ∀l : 1≤l≤j-1 C[i,l]=A[i,l]+B[i,l]} ∈ℕ ⇒ //fim do while j {j>m j ∧ ≤m+1 i ∧ ≤n k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l] ∧∀ ∈ ∧ ⇒ ∧ ℕ ∀l : 1≤l≤j-1 C[i,l]=A[i,l]+B[i,l]} ∈ℕ ⇒ {j=m+1 i ∧ ≤n k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l] ∧∀ ∈ ∧ ⇒ ∧ ℕ ∀l : 1≤l≤j-1 C[i,l]=A[i,l]+B[i,l]} ∈ℕ ⇒ {i≤n k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l] ∧∀ ∈ ∧ ⇒ ∧ ℕ ∀l : 1≤l≤m C[i,l]=A[i,l]+B[i,l]} ∈ℕ ⇒ {i≤n k,l : 1≤k≤i 1≤l≤m C[k,l]=A[k,l]+B[k,l]} ∧∀ ∈ ∧ ⇒ ℕ [último incluído no anterior, de 1 até i] ∀ ∀ 7 i = i + 1 {i≤n+1 k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]} ∧∀ ∈ ∧ ⇒ ℕ //fim do while i} {i>n i ∧ ≤n+1 k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]} ∧∀ ∈ ∧ ⇒ ℕ
  • 123.
    Exemplo 1 {i>n i ∧≤n+1 k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]} ∧∀ ∈ ∧ ⇒ ℕ {i=n+1 k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l]+B[k,l]} ∧∀ ∈ ∧ ⇒ ℕ { k,l : 1≤k≤n 1≤k≤m C[k,l]=A[k,l]+B[k,l] ∀ ∈ℕ ∧ ⇒ }
  • 124.
    Exemplo 1 ● A invariantede laço principal é i ≤ n+1 k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l] ∧∀ ∈ℕ ∧ ⇒ +B[k,l] 1 for i=1 to n 2 for j=1 to m 3 C[i,j] = A[i,j] + B[i,j] ‘ no final, mas depois do incremento de i.
  • 125.
    Exemplo 1 ● A invariantedo laço interno é j ≤ m+1 k,l : 1≤k≤i-1 1≤l≤m C[k,l]=A[k,l] ∧∀ ∈ℕ ∧ ⇒ +B[k,l] l : 1≤l≤j-1 C[i,l]=A[i,l]+B[i,l] ∧∀ ∈ℕ ⇒ 1 for i=1 to n 2 for j=1 to m 3 C[i,j] = A[i,j] + B[i,j] ‘ no final, mas depois do incremento de j e antes do incremento de i.
  • 126.
    Exemplo 1 Prova: A invariantedo laço principal é “para i ≤ n+1, para todo k, l , com 1≤k≤i-1 e 1≤l≤m, C[k,l]=A[k,l]+B[k,l].” ∈ℕ Inicialização: Após i=1 na linha 1, a invariante se mantém, pois nenhum elemento foi adicionado e o intervalo 1≤k≤i-1 é vazio. Além disso, como 0≤n, i=1 implica que i≤n+1. Manutenção: Após o teste do for na linha 1, i≤n. Para o while com j, temos a seguinte invariante de laço: "j≤m+1 e para todo k,l com 1≤k≤i-1 e 1≤l≤m, C[k,l]=A[k,l]+B[k,l] e para todo ∈ℕ l com 1≤l≤j-1, C[i,l]=A[i,l]+B[i,l]." ∈ℕ Inicialização do laço interno: Após j=1 da linha 2, como 0≤m, temos que j≤m+1. Como j=1, por vacuidade, temos que para todo para todo l com 1≤l≤j-1=0, C[i,l]=A[i,l]+B[i,l]. ∈ℕ
  • 127.
    Exemplo 1 Manuntenção dolaço interno: No teste do for na linha 2, temos j≤m. Após a linha 3, C[i,j] = A[i,j] + B[i,j] pode ser incluso na proposição para todo l com ∈ℕ 1≤l≤j-1, C[i,l]=A[i,l]+B[i,l], resultando em para todo l com 1≤l≤j, C[i,l]=A[i,l] ∈ℕ +B[i,l]. Após o incremento de j no for para j, a invariante do laço interno se mantém. Término do laço interno: Após o for da linha 2, antes do incremento do for para i, j=m+1. Assim, a invariante do laço interno torna-se para todo k,l com 1≤k≤i- ∈ℕ 1 e 1≤l≤m, C[k,l]=A[k,l]+B[k,l] e para todo l com 1≤l≤m, C[i,l]=A[i,l]+B[i,l]. Esta ∈ℕ é equivalente a para todo k,l com 1≤k≤i e 1≤l≤m, C[k,l]=A[k,l]+B[k,l], pois o ∈ℕ resultado para a linha i da matriz C é incluído no primeiro para todo que variava de 1 a i-1. Continuando o a manuntenção da invariante de laço principal, o resultado acima torna-se a invariante de laço principal após o incremento de i no for mais externo.
  • 128.
    Exemplo 1 Término: Apóso fim do for mais externo, i = n + 1 e a invariante de laço principal torna-se o resultado desejado, isto é, para todo k,l com 1≤k≤n e ∈ℕ 1≤l≤m, C[k,l]=A[k,l]+B[k,l].
  • 129.
    Exemplo 2 ● Demonstre queo INSERTION-SORT ordena um vetor de tamanho n≥1. {n≥1} INSERTION-SORT(A) 1 for j = 2 to n 2 key = A[j] 3 i = j - 1 5 while i > 0 and A[i] > key 6 A[i + 1] = A[i] 7 i = i - 1 8 A[i + 1] = key { k : 0<k<n A[k] ≤ A[k+1]} ∀ ∈ℕ ⇒
  • 130.
    Exemplo 2 INSERTION-SORT(A) 1 forj = 2 to A.length 2 key = A[j] 3 i = j - 1 5 while i > 0 and A[i] > key 6 A[i + 1] = A[i] 7 i = i - 1 8 A[i + 1] = key INSERTION-SORT(A) 0 j = 2 1 while j ≤ A.length 2 key = A[j] 3 i = j - 1 5 while i > 0 and A[i] > key 6 A[i + 1] = A[i] 7 i = i - 1 8 A[i + 1] = key 9 j = j + 1 Notação simplificada: ∀k : 0<k<n P(k) ∈ℕ ⇒ ⇔ 0<k<n P(k) ∀ ⇒
  • 131.
    Exemplo 2 {n≥1} INSERTION-SORT(A) {n≥1} 1 j= 2 {n+1≥2 j=2 1≤k≤0: A[k]≤A[k+1]} [Verdadeiro por vacuidade] ∧ ∧∀ {0<j≤n+1 1≤k≤j-2: A[k]≤A[k+1]} [0<2=j, j-2=0] ∧∀ 2 while j≤n {0<j≤n+1 j≤n 1≤k≤j-2: A[k]≤A[k+1]} ∧ ∧∀ {0<j≤n 1≤k≤j-2: A[k]≤A[k+1]} ∧∀ {j≤n 0<j 1≤k≤j-2: A[k]≤A[k+1] ∧ ∧∀ ∧ A[j]≤A[j] j≤k≤j-1: A[k]=A[k+1]} [a≤a, Verdadeiro por vacuidade] ∧∀ 3 key = A[j] 4 i = j - 1 {j≤n -1<i 1≤k≤i-1: A[k]≤A[k+1] key≤A[i+1] ∧ ∧∀ ∧ ∧ ∀i+1≤k≤j-1: A[k]=A[k+1]} {j≤n 0≤i 1≤k≤i-1: A[k]≤A[k+1] key≤A[i+1] ∧ ∧∀ ∧ ∧ [-1<i 0≤i] ⇒ ∀i+1≤k≤j-1: A[k]=A[k+1]}
  • 132.
    Exemplo 2 5 whilei>0 and A[i]>key {j≤n i>0 A[i]>key 0≤i 1≤k≤i-1: A[k]≤A[k+1] ∧ ∧ ∧ ∧∀ ∧ key≤A[i+1] i+1≤k≤j-1: A[k]=A[k+1]} ∧∀ {j≤n 0<i A[i]>key 1≤k≤i-2: A[k]≤A[k+1] A[i-1]≤A[i] ∧ ∧ ∧∀ ∧ ∧ ∀i+1≤k≤j-1: A[k]=A[k+1]} [i>0 0≤i→0<i, SIMP , A[i-1]≤A[i] extraído do ] ∧ ∧ ∀ {j≤n 0<i A[i]>key 1≤k≤i-2: A[k]≤A[k+1] i+1≤k≤j-1: A[k]=A[k+1]} ∧ ∧ ∧∀ ∧∀ [SIMP ] ∧ 6 A[i+1] = A[i] {j≤n 0<i A[i+1]>key 1≤k≤i-2: A[k]≤A[k+1] A[i]=A[i+1] ∧ ∧ ∧∀ ∧ ∧ ∀i+1≤k≤j-1: A[k]=A[k+1]} {j≤n 0<i key≤A[i+1] 1≤k≤i-2: A[k]≤A[k+1] ∧ ∧ ∧∀ ∧ i≤k≤j-1: A[k]=A[k+1]} ∀ [a<b→a≤b] 7 i = i – 1 {j≤n -1<i key≤A[i+2] 1≤k≤i-1: A[k]≤A[k+1] ∧ ∧ ∧∀ ∧ ∀i+1≤k≤j-1: A[k]=A[k+1]} {j≤n 0≤i key≤A[i+2] 1≤k≤i-1: A[k]≤A[k+1] A[i+1]=A[i+2] ∧ ∧ ∧∀ ∧ ∧ ∀i+2≤k≤j-1: A[k]=A[k+1]} [-1<i→0≤i, A[i+1]=A[i+2] extraído do ] ∀
  • 133.
    Exemplo 2 {j≤n 0≤ikey≤A[i+2] 1≤k≤i-1: A[k]≤A[k+1] A[i+1]=A[i+2] ∧ ∧ ∧∀ ∧ ∧ ∀i+2≤k≤j-1: A[k]=A[k+1]} {j≤n 0≤i 1≤k≤i-1: A[k]≤A[k+1] key≤A[i+1] i+1≤k≤j-1: A[k]=A[k+1]} ∧ ∧∀ ∧ ∧∀ [key≤A[i+2] A[i+1]=A[i+2]→key≤A[i+1]] ∧ //fim do while com i {¬(i>0 A[i]>key) j≤n 0≤i 1≤k≤i-1: [k]≤A[k+1] ∧ ∧ ∧ ∧∀ ∧ key≤A[i+1] i+1≤k≤j-1: A[k]=A[k+1]} ∧∀ {(i≤0 A[i]≤key) j≤n 0≤i 1≤k≤i-1: A[k]≤A[k+1] ∨ ∧ ∧ ∧∀ ∧ key≤A[i+1] i+1≤k≤j-1: A[k]=A[k+1]} ∧∀ {j≤n ( ∧ (i≤0 0≤i 1≤k≤i-1: A[k]≤A[k+1] key≤A[i+1] ∧ ∧∀ ∧ ∧ ∀i+1≤k≤j-1: A[k]=A[k+1])∨(A[i]≤key 0≤i 1≤k≤i-1: A[k]≤A[k+1] ∧ ∧∀ ∧ key≤A[i+1] i+1≤k≤j-1: A[k]=A[k+1] ∧∀ ))} {j≤n ( ∧ (i=0 1≤k≤i-1: A[k]≤A[k+1] key≤A[i+1] ∧∀ ∧ ∧ ∀i+1≤k≤j-1: A[k]=A[k+1])∨(0≤i 1≤k≤i-1: A[k]≤A[k+1] ∧∀ ∧ A[i]≤A[i+1] i+1≤k≤j-1: A[k]=A[k+1]) ∧∀ )} {j≤n ( ∧ (key≤A[1] 1≤k≤j-1: A[k]=A[k+1]) ∧∀ ∨ 1≤k≤j-1: A[k]≤A[k+1] ∀ )} [subst. i=0, SIMP ,a=b→a≤b ∧ ] 8 A[i+1] = key {j≤n ((i=0 ∧ A[i+1]≤A[1] 1≤k≤j-1: A[k]=A[k+1]) 1≤k≤j-1: A[k]≤A[k+1])} ∧ ∧∀ ∨∀
  • 134.
    Exemplo 2 {j≤n ((i=0A[i+1]≤A[1] 1≤k≤j-1: A[k]=A[k+1]) 1≤k≤j-1: A[k]≤A[k+1])} ∧ ∧ ∧∀ ∨∀ {j≤n 1≤k≤j-1: A[k]≤A[k+1]} [i=0 A[i+1]≤A[1]→V,a=b→a≤b] ∧∀ ∧ 9 j = j+1 {j≤n+1 1≤k≤j-2: A[k]≤A[k+1]} ∧∀ {j>n j≤n+1 1≤k≤j-2: A[k]≤A[k+1]} ∧ ∧∀ {j=n+1 1≤k≤j-2: A[k]≤A[k+1]} ∧∀ { k : 0<k<n A[k]≤A[k+1]} ∀ ∈ ⇒ ℕ
  • 135.
    Exemplo 2 INSERTION-SORT(A) 1 forj = 2 to A.length 2 key = A[j] 3 i = j - 1 5 while i > 0 and A[i] > key 6 A[i + 1] = A[i] 7 i = i - 1 8 A[i + 1] = key INSERTION-SORT(A) 0 j = 2 {invariante de laço} 1 while j ≤ A.length 2 key = A[j] 3 i = j - 1 5 while i > 0 and A[i] > key 6 A[i + 1] = A[i] 7 i = i - 1 8 A[i + 1] = key 9 j = j + 1 {invariante de laço}
  • 136.
    Exemplo 2 ● A invariantede laço principal é j ≤ n+1 0<k<j-1 A[k] ≤ A[k+1] ∧ ∀ ⇒ INSERTION-SORT(A) 1 for j = 2 to A.length 2 key = A[j] 3 i = j - 1 5 while i > 0 and A[i] > key 6 A[i + 1] = A[i] 7 i = i - 1 8 A[i + 1] = key ‘ no final, mas depois do incremento de j.
  • 137.
    Exemplo 2 ● A invariantedo laço interno é 0≤i 1≤k≤i-1: A[k]≤A[k+1] ∧∀ ∧ key≤A[i+1] i+1≤k≤j-1: A[k]=A[k+1] ∀ INSERTION-SORT(A) 1 for j = 2 to A.length 2 key = A[j] 3 i = j - 1 5 while i > 0 and A[i] > key 6 A[i + 1] = A[i] 7 i = i - 1 8 A[i + 1] = key ‘ depois do decremento de i
  • 138.
    Exemplo 2 Prova: A invariantedo laço principal é “para uma iteração j, o vetor A[1..j-1] está ordenado com j≤n+1.” Inicialização: Após j=2 na linha 1, a invariante se mantém, pois o único elemento é A[1] e j=2 implica que 0<j≤n+1, visto que é assumido que n≥1. Manutenção: Após a linha 1, 0<j≤n. Para o while da variável i, temos a seguinte invariante de laço: “0≤i, o vetor A[1..i] está ordenado, key≤A[i+1] e os elementos de A[i+1..j-1] foram copiados para A[i+2..j].” Inicialização do laço interno: Após a linha 4, a invariante de laço para i é verdadeira, pois como i=j-1 e 0<j≤n, então 0≤i. Além disso, o vetor A[1..j-1] já estava ordenado da invariante de laço principal, logo A[1..i] está ordenado. Temos que key=A[j] e A[j]≤A[j], então key≤A[j]=A[i+1]. Podemos dizer que A[i+1..j-1] foi copiado para A[i+2..j], visto que esses vetores são vazios, pois i+1>j-1.
  • 139.
    Exemplo 2 Manutenção dolaço interno: Após a linha 5, j≤n, 0<i e A[i]>key implica que key≤A[i]. Após a linha 6, key≤A[i+1] e A[i]=A[i+1]. Logo, com a invariante do while com i, temos agora que A[i..j-1] foi copiado para A[i+1..j]. Após a linha 7, 0≤i, key≤A[i+2] e A[i+1..j-1] foi copiado para A[i+2..j]. Como A[i+1..j-1] foi copiado para A[i+2..j], A[i+1]=A[i+2]. Logo, key≤A[i+1], concluindo a manuntenção do laço interno. Finalização do laço interno: Após o laço interno, i≤0 ou A[i]≤key. Para a primeira alternativa, se i≤0 e a invariante do while para i se mantém, então i=0. Logo, key≤A[1] e A[1..j-1] é igual a A[2..j]. Para a segunda alternativa, se A[i]≤key e a invariante do laço interno é verdadeira, A[i]≤key e key≤A[i+1] implicam que A[i]≤A[i+1]. Com isto e que A[1..i] está ordenado e A[i+1..j-1] foi copiado para A[i+2..j], temos que A[1..j] está ordenado. Após a linha 8, na primeira alternativa, key≤A[1] torna-se A[i+1]≤A[1]. Como i=0, esta desigualdade é sempre verdadeira. Como A[1..j-1] é igual a A[2..j] e key é A[1], então para a primeira alternativa também resulta em A[1..j] ordenado.
  • 140.
    Exemplo 2 Continuando amanuntenção do laço principal, após a linha 9, como A[1..j] ordenado e j≤n, têm-se que o vetor A[1..j-1] está ordenado com j≤n+1. Término: Quando o laço principal encerra, j>n. Pela invariante de laço, j>n e j≤n+1 implicam que j=n+1. Logo, A[1..n] está ordenado.
  • 141.
    Exercícios 1) Provar corretudedos algoritmos deste slide que não possuem pré- e pós- condições (MINMAX1, MINMAX2, ...). Observe que em algoritmos recursivos deve-se utilizar indução. Apresente também a prova em linguagem natural. 2) Faça o mesmo para os algoritmos a seguir:
  • 142.
    Exercícios {n>0} Fib(n) i, a, b= 1, 1, 0 while i < n i, a, b = i+1, a+b, a {a = fn} return a {n>0} maiorPotenciaDe2(n) i = 1 while 2i < n i = 2i {0 < i ≤ n< 2i p : i = 2 ∧∃ ∈ℕ p } return i f0 = 0 f1 = 1 fn = fn-1 + fn-2
  • 143.
  • 144.
    Exercícios 3) Mostre queo seguinte algoritmo realiza a multiplicação de dois números binários de tamanho n. Mostre também que o tempo no pior caso é O(n1,59 ). Dicas: – A prova não será por invariante de laço, pois o algoritmo é recursivo. – Seja x = 101101102. Então, n = 8, xL = 1011, xR = 0110 e x = 101101102 = 1011∙24 + 0110 = xL∙2n/2 +xR. ● Multiplicar por 2i pode ser feito com i deslocamentos à esquerda.
  • 145.
    Exercícios GAUSSMULT(x,y) 1 if n= 1 return xy 2 xL, xR = n/2 ⌈ ⌉ bits à esquerda de x, n/2 ⌊ ⌋ bits a direita de x 3 yL, yR = n/2 ⌈ ⌉ bits à esquerda de y, n/2 ⌊ ⌋ bits a direita de y 4 P1 = GAUSSMULT(xL,yL) 5 P2 = GAUSSMULT(xR,yR) 6 P3 = GAUSSMULT(xL+xR,yL+yR) 7 return P12n + (P3-P1-P2)2n/2 + P2
  • 146.
    Referência ● Gries, David. Thescience of programming. 1981. Springer-Verlag. ● Cormen, T.H., Leiserson, C.E., Rivest, R.L. Stein, C. Introduction to Algorithms. Third edition. 2009. MIT Press.