sábado, 10 de agosto de 2013

Rotinas para Torre de Hanói

Terminada a analise da torre de Hanói, se tem duas funções matemáticas que calculam o numero movimentos necessários para concluir o desafio:

Forma fechada:

Recorrente:


Transpor as expressões para o código requer pouco esforço, as rotinas movesR e movesC que fornecem a solução tanto pela recorrência, como pela forma fechada, seguem abaixo:

#include <stdio.h>
#include <math.h>

typedef unsigned int    uint;

uint movesR(uint n)
{
      if (n == 0)
            return 0;
      else
            return (2 * movesR(n-1) + 1);
}

uint movesC(uint n)
{
      return ((uint) pow(2.0, (double) n)) - 1 ;
}

void main(void)
{
      uint n;

      for (n = 0; n <= 8; ++n) {
            printf("movesR(%u) = %u\n", n, movesR(n));
            printf("movesC(%u) = %u\n", n, movesC(n));
      }
}

                Como a função recorrente para o total de movimentos foi obtida pela simulação de uma torre com 3 disco, está mesma função será usada para percorrer cada movimentos dos discos entre os pinos.

                Quando se criou a rotina recursiva movesR que calculava o numero de movimentos no disco, se adaptou do resultado obtido no exemplo de T(n) =2 * T(n) + 1, entretanto, perceba que estamos multiplicando o resultado como simplificação da expressão T(n) + 1 + T(n), apesar de manter o resultado do total de movimentos, perde-se a sequencia em que os movimentos foram realizados.

                Para corrigir a sequencia de disco, alteramos a rotina movesR, agora nomeada para hanoi, para executar a chamada recursivas duas vezes, indicando a ordem em que os discos foram movimentado:

uint hanoi(uint disk)
{
      if (disk == 0)
            return 0;
      else {
            uint moves;

            moves  = hanoi(disk-1);
            moves += 1;       printf("moveu disco %d\n", disk);
            moves += hanoi(disk-1);

            return moves;
      }
}

                Próxima informação é indicar os pinos de origem e destino que estão sendo usados, como entrada, incluindo também o pino temporário na chamada:

uint hanoi(uint disk, char src, char dst, char tmp)
. . .
      moves  = hanoi(disk-1, /* ? */, /* ? */, /* ? */);
      moves += 1;
      printf("moveu disco %d: ", disk);
      printf("pino %c para o pino %c\n", src, dst);
      moves += hanoi(disk-1, /* ? */, /* ? */, /* ? */);
. . .

                Para definir a seleção de pinos para a rotina, veja a simulação de para hanoi(2, X, Y, Z) e como vão ser executadas as duas chamadas recursiva para o disco 1:



X



Y



Z







































































































1. Movimenta o disco 1 do pino X para o pino Z, faz a primeira chamada recursiva da rotina indicando pino X como origem e o pino Z como destino resultando em hanoi( 1, X, Z, Y ).



X



Y



Z







































































































2. Instante em que imprime o movimento feito em hanoi( 2, X, Y, Z ).



X



Y



Z







































































































3. Chama pela segunda vez a rotina, temos indicado o movimento do disco, desta vez do pino de origem Z para o pino de destino Y com a função carregando os valores hanoi( 1,Z, Y, X ).



X



Y



Z







































































































                Na tabela vemos o processo de substituição das chamadas da simulação pelo nome das variáveis utilizadas na rotina:

hanoi( 2, X, Y, Z )
hanoi( disk, src, dst, tmp)
hanoi( 1, X, Z, Y )
hanoi( disk-1, src, tmp, dst )
hanoi( 1, Z, Y, X )
hanoi( disk-1, tmp, dst, src)

                Com a ordem dos pinos acertada para a primeira e a segunda chamada recursiva dentro da rotina, fica fácil entender a lógica, move-se o disco superior para o pino temporário, move o disco inferior para o destino e retorna novamente para o topo o disco que está no pino temporário.

uint hanoi(uint disk, char src, char dst, char tmp)
. . .
      moves  = hanoi(disk-1, src, tmp, dst);
. . .
      moves += hanoi(disk-1, tmp, dst, src);
. . .

                A rotina hanoi tem como objetivo gerar o caminho utilizado, podendo assim desprezar o contador de movimentos, simplificando ainda mais o código, o resultante completo fica:

#include <stdio.h>

typedef unsigned int    uint;

void hanoi(uint disk, char src, char dst, char tmp)
{
      if (disk != 0)
      {
            hanoi(disk-1, src, tmp, dst);

            printf("moveu disco %d: ", disk);
            printf("pino %c para o pino %c\n", src, dst);

            hanoi(disk-1, tmp, dst, src);
      }
}

void main(void)
{
      hanoi(4, 'X', 'Y', 'Z');
}


                Ao rodar o programa a seguinte saída é esperada:

C:\Projects\Hanoi\Debug>hanoi
moveu disco 1: pino X para o pino Z
moveu disco 2: pino X para o pino Y
moveu disco 1: pino Z para o pino Y
moveu disco 3: pino X para o pino Z
moveu disco 1: pino Y para o pino X
moveu disco 2: pino Y para o pino Z
moveu disco 1: pino X para o pino Z
moveu disco 4: pino X para o pino Y
moveu disco 1: pino Z para o pino Y
moveu disco 2: pino Z para o pino X
moveu disco 1: pino Y para o pino X
moveu disco 3: pino Z para o pino Y
moveu disco 1: pino X para o pino Z
moveu disco 2: pino X para o pino Y
moveu disco 1: pino Z para o pino Y



Nenhum comentário:

Postar um comentário