Recursão [Versão de Março de 2000] Definição Um objeto é dito recursivo se ele consistir parcialmente ou for definido em termos de si próprio Recursões ocorrem na matemática, informática, no dia a dia... Exemplos de recursão em definições matemáticas Números naturais: 0 é um número natural; O sucessor de um número natural é um número natural; Função Fatorial n! para inteiros positivos: 0! = 1 n>0: n! = n * (n - 1)! Programação II Cristiano André da Costa 2 Março de 2000 1
Algoritmos Recursivos Um algoritmo que para resolver um problema divide-o em subproblemas mais simples, cujas soluções requerem a aplicação dele mesmo, é chamado recursivo Em programação, uma subrotina (procedimento ou função) é recursiva quando ela chama a si mesma. Suponha uma rotina recursiva R formada por um conjunto de comandos C (que não contém chamadas a R) e uma chamada (recursiva) à R: R [C, R] Programação II Cristiano André da Costa 3 Tipos de Recursão 1. Recursão Direta 1. É quando em uma subrotina existe uma chamada para a própria subrotina, independentemente dos valores dos parâmetros: R [C, R] 2. Recursão Indireta 1. As subrotinas são conectadas através de uma cadeia de chamadas sucessivas que acaba retornando à primeira que a desencadeou: R 1 [C 1, R 2 ] R 2 [C 2, R 3 ]... R n [C n, R 1 ] Programação II Cristiano André da Costa 4 Março de 2000 2
Funcionamento da Recursão A subrotina permite que seja dado um nome a um conjunto de comandos. Um desses comandos pode ser a chamada à própria subrotina subrotinas possuem objetos locais sem significado fora dela variáveis, parâmetros, constantes, tipos e subrotinas Todo vez que tal subrotina for executada recursivamente, um novo conjunto de variáveis locais e parâmetros são criados Ainda que variáveis e parâmetros tenham o mesmo nome os identificadores se referem ao conjunto criado mais recentemente (seus valores são diferentes) Programação II Cristiano André da Costa 5 Funcionamento da Recursão 6. Algoritmo deve ser finito 6. Como ocorre nos comandos repetitivos, as chamadas recursivas possibilitam a não terminação (looping) 7. Para tal, deve ser condicionado uma expressão lógica que, em algum instante, torar-se-á false e permitirá que a recursão termine: R [C,T R], sendo T um teste lógico 6. Técnica básica para garantir o término: 6. definir uma função f(x), sendo x um conjunto de variáveis, tal que f(x) 0 implica em uma condição de terminação; 7. garantir que f(x) descresce a cada passo da repetição. R [C, (f(x)>0) R(x-1)], onde x decresce a cada chamada Programação II Cristiano André da Costa 6 Março de 2000 3
Funcionamento da Recursão Na prática, ao definir rotinas recursivas, o problema é dividido da seguinte forma: solução trivial: dada por definição; isto é, não necessita de recursão (resolvida pelo conjunto de comandos C) solução geral: parte do problema que em essência é igual ao problema original, porém menor (resolvida pela chamada recursiva R) Um teste define, a cada momento, se o problema terá solução trivial ou geral Em termos matemáticos, a recursão é uma técnica que, através de substituições sucessivas, reduz o problema a um caso de solução trivial Programação II Cristiano André da Costa 7 Exemplo de Recursão: fatorial Cálculo da Fatorial (números naturais positivos) solução trivial: 0! = 1 (condição de parada) solução geral: n! = n*(n-1)! Algoritmo em Pascal para cálculo da fatorial FUNCTION fat(n: INTEGER):INTEGER; BEGIN IF n= 0 THEN fat:= 1 ELSE fat:= n * fat (n-1); END; fat(4) 4*fat(3) 4*3*fat(2) 4*3*2*fat(1) 4*3*2*1*fat(0) 4*3*2*1*1 problema a resolver substituições sucessivas solução trivial obtida Programação II Cristiano André da Costa 8 Março de 2000 4
Profundidade da Recursão Profundidade é o número de vezes que uma rotina recursiva chama a si própria, até obter o resultado. Muitas vezes a profundidade de uma recursão não é tão clara, até mesmo para definições simples. Exemplo: profundidade da fatorial de 4: fat(4)=4*fat(3) fat(3)=3*fat(2) 1 o nível fat(2)=2*fat(1) 2 o nível fat(1)=1*fat(0) 3 o nível fat(0)=1 4 o nível Valores de n: da chamada é 4, do 1 o nível é 3, do 2 o nível é 2 do 3 o nível é 1 e do 4 o nível é 0. Programação II Cristiano André da Costa 9 Enunciado do problema Há muito tempo atrás, no alto das montanhas de Hanoi, havia um mosteiro onde habitavam sacerdotes brâmanes; entre eles, era praticado um ritual para predizer o fim do mundo. Conta a lenda, que no mosteiro havia três torres, sendo que na primeira delas estavam empilhados 64 discos de ouro em tamanhos decrescentes. Os sacerdotes acreditavam que quando eles terminassem de transferir todos os discos da primeira torre para a terceira (usando a segunda), sem nunca colocar um disco maior sobre um menor, então, neste dia, o mundo acabaria! Programação II Cristiano André da Costa 10 Março de 2000 5
Para facilitar, vamos supor a existência de três discos: Torre A Torre B Torre C O objetivo é transferir os três discos da torre A para a torre C, usando a torre B como auxiliar. Somente o primeiro disco de uma torre pode ser deslocado para outra, e um disco maior nunca pode ser colocado sobre outro menor. Programação II Cristiano André da Costa 11 Processo para transferir os três discos: 1 2 3 4 5 6 7 8 Programação II Cristiano André da Costa 12 Março de 2000 6
Matematicamente é possível demonstrar que o tempo necessário para mover n discos é da ordem de n! (ou maior). Solução usando recursão: solução trivial: Se n=1, transfira o disco da torre A para a torre C; solução geral: a) transfira n-1 discos da torre A para a torre B, usando C como auxiliar; b) transfira o último disco da torre A para a torre C; c) transfira n-1 discos da torre B para torre C, usando A como auxiliar. Programação II Cristiano André da Costa 13 Algoritmo em Pascal para a solução do problema PROCEDURE Hanoi(n:INTEGER; Org,Aux,Dst:CHAR); BEGIN IF n= 1 THEN writeln( Mova disco 1 da torre,org, para, Dst) ELSE BEGIN Hanoi(n-1,Org,Dst,Aux); writeln( Mova disco,n, da torre,org, para, Dst); Hanoi(n-1,Aux,Org,Dst); END; END; Programação II Cristiano André da Costa 14 Março de 2000 7
Quando a chamada Hanoi(3, A, B, C ) for executada, será produzida a seguinte saída: Mova disco 1 da torre A para C Mova disco 2 da torre A para B Mova disco 1 da torre C para B Mova disco 3 da torre A para C Mova disco 1 da torre B para A Mova disco 2 da torre B para C Mova disco 1 da torre A para C Programação II Cristiano André da Costa 15 Uso da Recursão Nem sempre é recomendado o uso de recursão: Alguns problemas são impossíveis de solucionar com recursão; Custo de tempo e espaço pois cada vez que a subrotina é chamada, todas as variáveis locais são recriadas (gerenciamento do registro de ativação e espaço por ele ocupado); Dificuldade na depuração de programas, particularmente se a recursão for profunda. Por que usar recursão? Se bem empregada, torna o algoritmo muito elegante, isto é, claro, simples e conciso. Programação II Cristiano André da Costa 16 Março de 2000 8
Uso da Recursão Algoritmos recursivos são apropriados quando o problema for definido em termos recursivos Programas onde a recursão deve ser evitada podem ser evitados por um esquema que exiba o padrão da sua composição: R [T C,R] R [C,T R] a característica desses programas é possuir uma só chamada a R no final. Chamada de Recursão de Cauda os programas que se encaixam nesse esquema, como o exemplo do cálculo da fatorial, possuem uma solução iterativa (não recursiva) mais eficiente. Programação II Cristiano André da Costa 17 Recursão de Cauda Na recursão de cauda a chamada recursiva está no final do código, tendo como função criar um laço que será repetido até a condição de parada. Para eliminar a recursão de cauda: Se uma rotina R(x) tem como última instrução uma chamada recursiva R(y), então troca-se R(y) pela atribuição x y, seguido de um desvio para o início de R. Utiliza-se uma repetição condicionada à expressão de teste usada na versão recursiva. Programação II Cristiano André da Costa 18 Março de 2000 9
Versão Iterativa do Cálculo de fatorial Algoritmo em Pascal FUNCTION fat(n:integer):integer; VAR result:integer; BEGIN result:=1; WHILE n>0 DO BEGIN result:= result*n; n:= n-1; END; fat := result; END; Embora fatorial seja mais eficiente de forma iterativa, é um excelente exemplo para se entender o que é recursão Programação II Cristiano André da Costa 19 Março de 2000 10