• Nenhum resultado encontrado

5.5 Verificação d e Programas

5.5.4 As Torres de Hanó

Concluímos esta seção sobre recursão, indução e verifi­ cação de programas com um quebra-cabeça matemático clássico: as Torres de Hanói. Vale a pena refletirmos

sobre esse quebra-cabeça porque ele ilustra o poder e a elegância do pensamento recursivo, e porque ele nos dá um exemplo de um algoritmo fácil de ser verificado, mas difícil de ser testado.

0 quebra-cabeça consiste em três pinos e uma pilha de discos de tamanhos decrescentes. (Veja a Figura 5.17.) Para começar, todos os discos estão empilhados por ordem de tamanho no pino mais à esquerda, com o disco maior embaixo. O objetivo é mover toda a pilha de discos para o pino mais à direita, respeitando as seguintes regras:

1. O disco do topo em qualquer um dos pinos pode ser movido para qualquer dos outros dois pinos, onde neles se torna o disco do topo.

2. Apenas um disco pode ser movido por vez. 3. Um disco nunca pode ser colocado em cima de um

disco menor.

A fim de discutirmos como resolver esse quebra- cabeça, devemos estabelecer alguma notação. Numere os três pinos da esquerda para a direita como 1, 2 e 3. Um único movimento de um disco de um pino para outro pode então ser representado por um par ordenado (p,

q), com {p, q} Ç {1, 2, 3}. Por exemplo, a Figura 5.18

ilustra a sequência de movimentos (1,2), (1,3), (2,1) no caso n = 4.

Aqui, nosso objetivo é escrever e demonstrar a correção de um algoritmo para resolver esse quebra- cabeça para qualquer valor de n. Enquanto pensamos sobre como escrever o algoritmo, podemos pensar ante­ cipadamente em como será a demonstração indutiva da correção. O caso base é fácil de ser resolvido: quando há apenas n = 1 disco, podemos simplesmente movê-lo para o pino mais à direita. A hipótese indutiva será,

grosso modo, que podemos resolver o quebra-cabeça para

Figura 5.17 Uma versão do quebra-cabeça Torres de Hanói com n = 5 discos.

(1,2) (1,3) (2,1)

Al I Al I Al 1 Al 1

Figura 5.18 Esta sequência de movimentos é denotada por (1, 2), (1, 3), (2, 1).

n = k — 1 discos. O truque para resolvermos o caso de n = k discos é usar o caso n = k — 1 para fazer a maior

parte do trabalho. Suponha que k discos estão inicial­ mente empilhados no pino nQ 1.

• Mova os discos k — 1 do topo do pino nQ 1 para o pino nQ 2, usando a solução para n = k — 1. • Mova o disco restante do pino n° 1 para o pino

nQ 3.

• Use o caso n = k — 1 novamente para mover a pilha do pino nQ 2 para o pino nQ 3.

A versão formal em pseudocódigo desse algoritmo, usando a notação de pares ordenados anterior mente, é mostrada no Algoritmo 5.19.

A avaliação de cima para baixo a seguir testa esse algoritmo para o caso n = 2. Inicialmente, os dois discos estão empilhados no pino nQ 1.

Hanoi(2,1,3) = H a n o i (1,1,2), (1,3), Hanoi(l,2,3) = (1,2), (1,3), (2,3)

Essa sequência de movimentos, de fato, resolve o quebra- cabeça para o caso n = 2. Testar o caso n = 3 é deixado como exercício, assim como demonstrar a correção desse algoritmo para todo n.

Um outro exercício irá pedir que você verifique que essa função sempre retorna uma sequência de 2n — 1 movimentos. Portanto, por exemplo, o nosso algoritmo irá exigir

1.267.650.600.228.229.401.496.703.205.375 movimentos para resolver o quebra-cabeça no caso de

n = 100 discos. Suponha (generosamente) que um

computador seja capaz de produzir e testar essa sequ­ ência em uma velocidade de um rianossegundo por movimento. Tal teste levaria cerca de 40 trilhões de anos! Esse cálculo ilustra o poder da verificação de programa. Testar esse programa — mesmo para valores relativamente pequenos de n — é praticamente impos­ sível, mas demonstrar a sua correção para todos os valores de n é fácil.

A lgoritm o 5.19 Resolvendo o quebra-cabeça das Torres de Hanói.

Condições prévias: n £ N, pde, ppara {1, 2, 3}, pde A

PpíLTÍi'

Condições posteriores: Retorna uma sequência de pares ordenados que represente movimentos legais que resolvam o quebra-cabeça das Torres de Hanói para

n discos, em que os discos são inicialmente empi­

lhados no pino pde e movidos para o pino ppara.

função Hanoi(n £ N, pdc, ppara £ {1, 2, 3})

se n = 1 então

retornar (pde, ppara) senão

r Poutro outro pino (que não pde ou ppara) retornar Hanoi(n - 1, pde, poutro),

de; P p a r a ) )

l Hanoi(n - 1, poutro, ppara)

E x e rc íc io s 5.5

1. O teste de programa pode demonstrar que um algoritmo não está correto? Explique.

2. A demonstração da correção do algoritmo Reversa

no Exemplo 5.19 usa notações diferentes daquelas usadas na demonstração do Teorema 3.4. O que mais é diferente sobre essas duas demonstrações? 3. Veja o Exemplo 5.21. A demonstração da correção

do algoritmo de busca binária usa qual tipo de indução (simples ou forte)? Explique.

4. Suponha que uma função recursiva Mediana^,

..., xn £ R ) tem as seguintes condições prévias e posteriores.

Condições prévias: x2, ..., xn £ R .

Condições posteriores: Retorna a mediana de x1,

Responda às seguintes perguntas considerando uma possível demonstração de correção por indução.

(a) Qual é o caso base?

(b) Qual é a hipótese indutiva, supondo que a demonstração usa indução simples?

(c) Qual é a hipótese indutiva para indução forte?

(d) O que você teria que mostrar, então, para terminar a demonstração?

5. Seja U um conjunto cujos elementos podem ser colocados em uma árvore de busca binária. Uma função recursiva FazerA rvore(w1, «2, ..., un E U)

para fazer uma árvore de busca binária tem as seguintes condições prévias e posteriores.

Condições prévias: uu «2, un E U.

Condições posteriores: Retornar uma árvore de busca binária cujos vértices são w1; u2, ..., un. Responda às seguintes questões considerando uma possível demonstração de correção por indução. (a) Qual é o caso base?

(b) Qual é a hipótese indutiva para indução simples?

(c) Qual é a hipótese indutiva para indução forte?

(d) O que você teria que mostrar, então, para terminar a demonstração?

6. Demonstre que o algoritmo a seguir está correto. Condições prévias: n é um número natural ímpar. Condições posteriores: Quadrado (n) =

função Quadrado(n E N) se n = 1 en tão

r e t o r n a r 1 senão

r e t o r n a r n + Quadrado(n — 2)

7. Demonstre que o algoritmo a seguir está correto. Condições prévias: n> 0. Condições posteriores: P(n) = 2 — função p(n E Z) se n = 0 en tão r e t o r n a r 1 senão 1 r e t o r n a r 1 + - - P ( n —1) 8 * * * *

8. No Exercício 17 da Seção 5.1, você escreveu uma versão em pseudocódigo da relação de recorrência para números hexagonais e deu condições prévias e posteriores descritivas. Demonstre que o seu algo­ ritmo está correto.

n + 1

9. Demonstre que o algoritmo a seguir está correto. Condições prévias: n = 2P para algum inteiro

p > 0.

Condições posteriores: PLog(n) = log2 n. função PLog(n E N)

se n = 1 então

retornar 0

senão

retornar 1 + Plog(n/2)

*10. Demonstre que o algoritmo a seguir está correto. Condições prévias: n S 1.

Condições posteriores: iLog(n) = l_log2 nj. função ILog(n E N)

se n — 1 então

retornar 0

senão

retornar 1 + Ilog(n/2)

11. Prove que o algoritmo a seguir para calcular potên­ cias está correto.

Condições prévias: n ^ 0, x E R.

Condições posteriores: Potência(:r, n) = af. função Potência(a; E R, n E Z)

se n = 0 então

retornar 1

senão

retornar x • Potência(rr, n — 1)

12. O algoritmo a seguir é uma versão “mais rápida” do algoritmo no Problema 11. Note que as condições prévias e posteriores são as mesmas. Demonstre que esse algoritmo mais rápido está correto. Condições prévias: n ^ 0, x E R.

Condições posteriores: QPotência(a:, n) = af1.

função QPotência(a: E R, n e Z) se n = 0 então retornar 1 senão r se u é par então retornar (QPotência(.x, n/2))2 senão L retornar x • (QPotência(a;, u /2j)r 13. Recorra aos algoritmos nos Problemas 11 e 12.

Use avaliações de cima para baixo para calcular o seguinte.

(a) Potência(2, 10)

14. Demonstre que o percurso em ordem do Algoritmo 5.5 visita cada vértice na árvore exatamente uma vez.

15. Prove que a função BuscarMax do Algoritmo 5.10

está correta.

16. Prove que o algoritmo de busca sequencial recur­ siva a seguir está correto.

A lgoritm o 5.20 Busca sequencial recursiva.

Condições prévias: O conjunto U é totalmente orde­ nado por <, e X = {xu £>, ..., xn} é um subconjunto (possivelmente vazio) de U.

Condições posteriores: RBusca(í, X) = (t £ X).

função RBusca(í E U, X <z U)

se X = 0 então