Recursão e Back-Tracking Fernando Silva & Luís Lopes DCC-FCUP Estruturas de Dados
Recursão Uma função diz-se recursiva quando se chama a si própria, directa ou indirectamente. A recursão: à semelhança dos ciclos, permite repetir a execução de um conjunto de instruções é uma técnica de programação poderosa que permite frequentemente formular problemas complexos de forma simples está na base de uma técnica algorítmica designada por dividir-para-conquistar, em que um problema é decomposto em sub-problemas idênticos de menor complexidade, aplicando-se a cada instância o mesmo algoritmo de resolução.
A função factorial 1 se n=0 n! = fact(n) = n fact(n 1) se n>0 Exemplo: 5! = 5 4 3 2 1 = 5 (4 3 2 1) = 5 4! Importante: o 0! = 1 funciona como caso base da recursão, Uma função recursiva deve: ter pelo menos um caso base que pára a recursão as chamadas recursivas devem aproximar-se do caso base
Implementação Factorial c l a s s TestFactorial { p u b l i c s t a t i c v o i d main ( String args []) { System. out. println (" fact (9) = " + fact (9)); p r i v a t e s t a t i c i n t fact ( i n t n) { i f ( n == 0 ) r e t u r n 1; e l s e r e t u r n n * fact ( n - 1 );
A sequência de Fibonacci 1 se n=0 fib(n) = 1 se n=1 fib(n 1) + fib(n 2) se n>1 Exemplo: fib(3) = fib(2)+fib(1) = (fib(1)+fib(0))+fib(1) = (1+1)+1 = 3 neste caso a base da recursão é composta por fib(0) = 1 e fib(1) = 1 a sequência é 1, 1, 2, 3, 5, 8, 13, 21, 34, 55,...
Implementação Fibonacci c l a s s TestFibonacci { p u b l i c s t a t i c v o i d main ( String args []) { System. out. println (" fib (9) = " + fib (9)); p r i v a t e s t a t i c i n t fib ( i n t n) { i f ( n == 0 n == 1 ) r e t u r n 1; e l s e r e t u r n fib ( n - 1 ) + fib ( n - 2);
Mais Exemplos... p r i v a t e s t a t i c i n t recsum ( i n t [] a, i n t n) { i f (n == 1) r e t u r n a [0]; e l s e r e t u r n recsum (a, n - 1) + a[n - 1];...
Mais Exemplos...... p r i v a t e s t a t i c v o i d recinvert ( i n t [] a, i n t i, i n t j) { i f ( i < j ) { i n t tmp = a[i]; a[i] = a[j]; a[j] = tmp ; recinvert (a, i + 1, j - 1);...
Mais Exemplos...... p r i v a t e s t a t i c i n t recbinarysum ( i n t [] a, i n t i, i n t j) { i f ( i == j ) r e t u r n a[i]; e l s e { i n t k = (i + j) / 2; r e t u r n recbinarysum (a,i,k) + recbinarysum (a,k + 1,j);...
Diagrama de chamadas para um exemplo anterior com 8 elementos
Um último exemplo: pesquisa binária p r i v a t e s t a t i c i n t binarysearch ( i n t [] values, i n t val ) { r e t u r n binarysearch ( values, val, 0, values. length - 1); p r i v a t e s t a t i c i n t binarysearch ( i n t [] values, i n t val, i n t low, i n i f ( high < low ) r e t u r n -1; e l s e { i n t half = low + ( high - low ) / 2; i f ( val == values [ half ] ) r e t u r n half ; e l s e i f ( val < values [ half ] ) r e t u r n binarysearch ( values, val, low, half - 1); e l s e r e t u r n binarysearch ( values, val, half + 1, high );
Torres de Hanoi Jogo do século XIX associado a uma história da construção do Templo de Brahma. Dadas 3 pegas em diamante, uma das quais com 64 discos em ouro, foi pedido aos monges que deslocassem todos discos da 1 a para a 3 a pega, eventualmente usando a 2 a pega, mas sem nunca colocarem um disco maior em cima de um menor. De acordo com a lenda, quando o último disco fosse colocado o mundo terminaria.
Torres de Hanoi Problema: dadas 3 torres A, B e C, como deslocar os discos de A para C, eventualmente usando B, deslocando um disco de cada vez e sem nunca colocar um disco maior em cima de um menor.
Torres de Hanoi: uma solução objectivo: partindo de uma configuração inicial chegar a uma configuração final, deslocando um disco de cada vez e sem nunca colocar um disco maior em cima de um menor:
Torres de Hanoi: uma solução Suponhamos que conseguimos passar N-1 discos de A para B, usando C como temporário, ficando: Para chegar à solução final, os movimentos seguintes seriam: passar o disco maior de A para C passar os N-1 discos de B para C usando A como temporário O número mínimo de movimentos para chegar à solução é 2 N 1, para N discos.
Torres de Hanoi c l a s s TowersOfHanoiTest {... s t a t i c v o i d movedisks ( i n t n, char from, char to, char aux ) { i f ( n == 1 ) { // one disk remaining System. out. printf (" Move disk from %c to %c%n",from,to ); numbermoves ++; e l s e { movedisks ( n - 1, from, aux, to ); // n-1 from A to B using C movedisks ( 1, from, to, aux ); // largest disk from A to C movedisks ( n - 1, aux, to, from ); // n-1 from B to C using A
Torres de Hanoi c l a s s TowersOfHanoiTest { s t a t i c i n t numbermoves = 0; p u b l i c s t a t i c v o i d main ( String [] args ) { Scanner stdin = new Scanner ( System. in ); System. out. println (" Number of disks in the tower?"); i n t n = stdin. nextint (); movedisks (n, A, C, B ); // start moving disks System. out. println (" movimentos = " + numbermoves );...
Torres de Hanoi: árvore de recursão A árvore de recursão correspondente à chamada movedisks(3, A, C, B ), representando-se apenas os argumentos do método. Na figura, A->C significa que um disco da torre A é deslocado para a torre C.
Retrocesso (backtracking) esta técnica é normalmente usada para se encontrar todas as soluções de um problema, por construção de soluções parciais o algoritmo procura extender a solução parcial até estar completa, mas se alguma inconsistência acontecer o algoritmo retrocede (faz backtracking), removendo a parte da solução mais recentemente construída e tenta outra possibilidade
Exemplo: encontrar saída de labirinto Input: 6 6 S #####...# #.#### #.####...#. G ##...#
Exemplo... p u b l i c c l a s s SolveMaze { s t a t i c i n t nl, nc; s t a t i c i n t si, sj; s t a t i c char [][] maze ; p u b l i c s t a t i c v o i d main ( String args []) throws IOException { File file = new File ( args [0]); Scanner input = new Scanner ( file ); nl = input. nextint (); nc = input. nextint (); maze = new char [nl ][ nc ];...
Exemplo... p u b l i c c l a s s SolveMaze {... p u b l i c s t a t i c v o i d main ( String args []) throws IOException {... input. nextline (); f o r ( i n t i = 0 ; i < nl ; i++ ) { String line = input. nextline (); f o r ( i n t j = 0 ; j < nc ; j++ ) { char c = line. charat ( j); i f ( c == S ) { si = i; sj = j; maze [i][j] = c; i f ( findpath (si,sj) ) System. out. println (" found ");
Exemplo... p r i v a t e s t a t i c boolean findpath ( i n t i, i n t j) { printmaze (); i f ( i < 0 j < 0 i > nl - 1 j > nc - 1) r e t u r n f a l s e ; i f ( maze [i][j] == # maze [i][j] == + ) r e t u r n f a l s e ; i f ( maze [i][j] == G ) r e t u r n t r u e ; maze [i][j] = + ; i f ( findpath (i+1, j ) ) r e t u r n t r u e ; i f ( findpath (i -1, j ) ) r e t u r n t r u e ; i f ( findpath (i, j +1) ) r e t u r n t r u e ; i f ( findpath (i, j -1) ) r e t u r n t r u e ; maze [i][j] =. ; r e t u r n f a l s e ;
Exemplo... p r i v a t e s t a t i c v o i d printmaze () { f o r ( i n t i = 0 ; i < nl ; i++ ) { f o r ( i n t j = 0 ; j < nc ; j++ ) System. out. print ( " " + maze [i][j] + " "); System. out. println (); System. out. println (); System. out. println ();