Recursividade

Alguém com muita paciência pode me explicar como funciona recursividade?
Já vi em vários livros e tutorias e estou com um grande dificuldade para assimilar o assunto bem tenho um exemplo a qual eu peguei do livro
que mostra a serie de Fibonacci entendi certas partes do código e gostaria que vcs me explicasse melhor o que está ocorrendo…
vo deixar o código logo abaixo para ficar mais fácil o entedimento

public class FibonacciCalculator
{
   // declaração recursiva do método fibonacci 
   public long fibonacci( long number )                           
   {                                                              
      if ( ( number == 0 ) || ( number == 1 ) ) // casos básicos
         return number;                                           
      else // passo da recursão                                      
         return fibonacci( number - 1 ) + fibonacci( number - 2 );
   } // fim do método fibonacci

   public void displayFibonacci()
   {
      for ( int counter = 0; counter <= 3; counter++ )
         System.out.printf( "Fibonacci of %d is: %d\n", counter,
            fibonacci( counter ));
   } // fim do método displayFibonacci
} // fim da classe FibonacciCalculator
r 

classe main

public class FibonacciTest
{     
   public static void main( String args[] )
   {
      FibonacciCalculator fibonacciCalculator = new FibonacciCalculator();
      fibonacciCalculator.displayFibonacci();
   } // fim de main
} // fim da classe FibonacciTest

Bem pelo o que eu entendi recursividade e um método que chama ele mesmo dentro do seu corpo mas diminuindo valores certo…?
isso que eu venho observado em todos os exemplos que eu vejo,
exemplo neste método fibonacci a qual coloquei como modelo,
o que eu não entendo e a chamada de recursividade vo colocar como eu acho que o método fibonacci trabalha
fibonacci de (3)
[quote]
fibonacci(3)
retorna fibonacci(2) fibonacci (1)
fibonacci(2) retorna 1 fibonacci (1) retorna 0

              como sempre fibonacci de 0 e 1 retorna seus respectivos                     
                valores vão retornar 0 e 1;

[/quote]

A formula para calcular Fibonaci e a seguinte
fibonacci(0)=0
fibonacci(1)=1
fibonacci(n)= fibonacci(n-1)+fibonacci(n-2)

como vcs podem ver dentro do próprio método sempre o que o Fibonacci for de 0 e um retorna ele mesmo, mas se for de 3 ele divide a número certo…? mas como ocorre essa chamada…?? se for Fibonacci de 10 como é feito…? nesta parte do código que estou com grandes dúvidas

else // passo da recursão                                      
         return fibonacci( number - 1 ) + fibonacci( number - 2 );

como o método vai ficar repetindo por qual razão…? será que é enquanto não chegar ao fibonacci de 0 e 1 ele vai repetindo é isso…? como séria feito em um Fibonacci de 10…?

Se eu entendi bem a sua duvida, vc gostaria de saber como usa recursao?

Bom a recursao é quando chama o metodo dentro dele mesmo (Dentro de seu proprio escopo).

Em geral metodos recursivos podem ser implementados por um loop (for, while) e vice-versa.

Outra coisa, vc disse que vai "diminuindo valores certo…? "
Isso não é sempre. Como eu disse é como um loop, em geral os loop’s vao crescendo e metodos recursivos vao diminuindo valores mas isso não é uma regra.

Quanto ao fibo de 10 é feito assim:

fibo(10) = fibo(9) + fibo( 8 )
fibo(9) = fibo( 8 ) + fibo(7)

fibo(1) = 1
fibo(0) = 0
e assim em diante.

Espero que vc tenha entendido a recursao…

Outra coisa essa forma de fibonacci pode ser melhorada!
Caso vc realmente tenha grande interesse em aprender recursao, pegue algum material de programacao funcional é um outro paradigma de programação, porem explica muito bem como usar recursao (sugestao de linguagem funcional: Haskell)

caso ainda tenha duvidas, pergunta ai… falow…

blz cara deu para ter uma noção mas onde são armazernados os valores
essa e minha grande dúvida por exemplo

fibonacci (3) que na verdade é 2;

eu sei que nesta parte do programa faz o seguinte

 return fibonacci( number - 1 ) + fibonacci( number - 2 );

pega o fibonacci de 2 e soma com fibonacci de 1 certo…?

o fibonaci de 2 é 1 e o fibonacci de 1 e ele mesmo mais como o programa armazena esses valores…??? como ele sabe que o fibonaci de 2 é 1…??

é como uma pilha onde são armazenadas as chamadas ao seu método recursivo.
Vc vai notar q existe uma condição onde qdo for 0 ou 1 ele retorna o próprio parâmetros senão chama novamente o método recursivamente (vc sempre terá uma condição assim, pois o método tem q retornar o valor para o solicitante em algum momento … senão fica executando recursivamente para sempre :?
o princípio será sempre este, seja java, oracle forms ou c++.

muito obrigado

[quote=leo_ap]Se eu entendi bem a sua duvida, vc gostaria de saber como usa recursao?

Bom a recursao é quando chama o metodo dentro dele mesmo (Dentro de seu proprio escopo).

Em geral metodos recursivos podem ser implementados por um loop (for, while) e vice-versa.

Outra coisa, vc disse que vai "diminuindo valores certo…? "
Isso não é sempre. Como eu disse é como um loop, em geral os loop’s vao crescendo e metodos recursivos vao diminuindo valores mas isso não é uma regra.

Quanto ao fibo de 10 é feito assim:

fibo(10) = fibo(9) - fibo( 8 )
fibo(9) = fibo( 8 ) - fibo(7)

fibo(1) = 1
e assim em diante.

Espero que vc tenha entendido a recursao…

Outra coisa essa forma de fibonacci pode ser melhorada!
Caso vc realmente tenha grande interesse em aprender recursao, pegue algum material de programacao funcional é um outro paradigma de programação, porem explica muito bem como usar recursao (sugestao de linguagem funcional: Haskell)

caso ainda tenha duvidas, pergunta ai… falow…[/quote]

Oi!!
Estou com muita dúvida nesse codigo, não consigo entender, por que 3 é fibo 2 e 4 é fibo3. Alguém poderia me reponder por favor

[quote=Alessanndro Morais]…
Oi!!
Estou com muita dúvida nesse codigo, não consigo entender, por que 3 é fibo 2 e 4 é fibo3. Alguém poderia me reponder por favor
[/quote]
A dificuldade é em entender o código ou a sequência de Fibonacci?

Até!

[quote=maquiavelbona][quote=Alessanndro Morais]…
Oi!!
Estou com muita dúvida nesse codigo, não consigo entender, por que 3 é fibo 2 e 4 é fibo3. Alguém poderia me reponder por favor
[/quote]
A dificuldade é em entender o código ou a sequência de Fibonacci?

Até![/quote]

Oi!
A dificuldade é na sequência de Fibonacci. Nessa parte do codigo…
else { // passo da recursão
return fibonacci( number - 1) + fibonacci( number - 2 );
}

Pq vc não usa um exemplo mais simples para tentar entender?
Por exemplo, como calcular o fatorial de um número usando recursão?

Vc conhece a definição de fatorial?
O fatorial é um valor que é obtido pela multiplicação de um dado número por cada um dos seus antecessores até o 1.
Por exemplo, o fatorial de 3 (denotado como 3! - lê-se “três fatorial”) é:
3 * 2 * 1 = 6

Fatorial de 4 (4!)
4 * 3 * 2 * 1 = 24

Sendo que o fatorial de 0 (0!) por definição é 1.

Entendido o fatorial, vamos para a recursão…

A recursão é obtida quando um método chama a sí próprio n vezes para resolver um problema.
Esse problema deve ter um caso básico, que é o ponto onde o método irá parar de chamar a sí próprio.
No problema do fatorial, qual seria o caso básico?

O caso básico é quando o valor a ser multiplicado for 0, concorda?

Sendo assim, olha o código

[code]
public static long fatorial( n ) {

// caso básico, quando encontrado, retorna o valor do caso 

// básico e não chama mais o método
if ( n == 0 ) {

    return 1;

} else { // caso não seja zero, retorna n + fatorial( n - 1 )

    // n valor atual, n - 1 anterior
    return n * fatorial( n - 1 );

}

}[/code]

Teste de mesa para 3!

fatorial( 3 ) - 3 não é caso básico, chama fatorial de novo
3 * fatorial( 3 - 1 ) - 2 não é caso básico, chama fatorial de novo
2 * fatorial( 2 - 1 ) - 1 não é o caso básico, chama fatorial de novo
1 * fatorial( 1 - 1 ) - 0 é o caso básico, retorna 1

Agora leia de trás para a frente a execução
( 0 )
retorna 1
( 1 )
retorna 1 * 1 = 1
( 2 )
retorna 2 * 1 = 2
( 3 )
retorna 3 * 2 = 6 = 3!

E assim por diante.

Fico umais fácil agora?

Até mais!

Acho que as pessoas não entendem direito recursão porque em vez de usar um exemplo prático (método Paulo Freire e ensinar a ler com a palavra “tijolo”) usa-se um exemplo matemático simples (método cartilha Caminho Suave e ensinar a ler com a palavra “lua”).

Por exemplo, algo que as pessoas sempre precisam é de listar diretórios e subdiretórios recursivamente. Isso é interessante e sempre você acaba precisando; não de um fatorial (que pode ser calculado sem recursão, afinal de contas).

Usando a idéia do thingol, fiz um exemplo simples de como listar todos os diretórios e subdiretórios de uma pasta.

Pensem: se vcs quisessem listar TODAS as pastas de uma unidade, como fariam manualmente? Bom, vcs teriam que uma a uma, entrar dentro dela, e ir entrando dentro de cada subpasta da pasta inicial. E depois dentro de cada subpasta da subpasta anterior e assim até não haver mais subpastas na primeira pasta, pra só então ir pra segunda pasta. É isso que o código que eu fiz faz, de forma recursiva. Basicamente, imagine que temos essa árvore de diretórios em nossa unidade c:

pasta1 pastaA pastaD pastaE pastaB pasta2 pastaC

O sistema pega o primeiro nível de pastas: pasta1 e pasta2. Então ele percorre essas pastas selecionadas. Ele entra na pasta1 e pega as pastas existentes: pastaA e pastaB. Ai ele novamente entra na primeira pasta selecionada, a pastaA. Dentro dela ele pega a pastaD e a pastaE. Novamente ele entra na primeira selecionada, a pastaD. Dentro dela ele não encontrada nada. Então ele sai e vai pra próxima naquele nível, a pastaE. Dentro dela tb não encontra nada. Ele sai e volta um nível, vai pra pastaB. Dentro da pastaB tb não tem nada. Ele volta um nível, vai pra pasta2. Então ele vê que dentro tem a pastaC e pega ela. Ai ele começa a percorrer a pastaC e não encontra nada. Ele volta um nível. Vê que não tem mais nada pra percorrer nesse nível (já que pasta1 e pasta2 já foram percorridas). Então ele termina o processamento.

Explicando acho complicado de entender. Tentem debugar o fonte abaixo. Boa sorte! :wink:

[code]
import java.io.File;

/**

  • Esta classe visa imprimir os nomes dos diretórios de forma recursiva.
  • @author Renata

*/
public class Recursividade {

/**
 * Método principal.
 * @param args
 */
public static void main(String[] args) {
	
	// imprimo a árvore de diretórios a partir do diretório passado abaixo
	carregaDiretorios("C:\"); // se quiser comecar de outra pasta específica, é só colocar o nome dela aqui
}

/**
 * Método recursivo que carrega os diretórios/arquivos que estão dentro do diretório passado como parâmetro.
 * @param nomeDiretorio Diretório corrente
 */
private static void carregaDiretorios(String nomeDiretorio){
	
	System.out.println(nomeDiretorio); // imprimo o nome do diretório atual
	
	File dirAtual = new File(nomeDiretorio); // crio um objeto para o diretorio atual (dirAtual)
	File[] dirInternos = dirAtual.listFiles(); // crio um array com os diretórios/arquivos que estão dentro do dirAtual 

	if (dirInternos != null){ // se tiver encontrado algo dentro do diretório atual
		for (int i = 0; i < dirInternos.length; i++){ // percorro tudo que foi encontrado com esse loop
			if (dirInternos[i].exists() && dirInternos[i].isDirectory()) // verifico se o diretório/arquivo existe e se é um diretório (se for arquivo não vai entrar nesse IF, então será desprezado)
				carregaDiretorios(dirInternos[i].getPath()); // se é um diretório eu chamo o mesmo método em que estou para tratar esse diretório, isso é RECURSIVIDADE!
		}
	}
}

}[/code]

[quote=thingol]Acho que as pessoas não entendem direito recursão porque em vez de usar um exemplo prático (método Paulo Freire e ensinar a ler com a palavra “tijolo”) usa-se um exemplo matemático simples (método cartilha Caminho Suave e ensinar a ler com a palavra “lua”).

Por exemplo, algo que as pessoas sempre precisam é de listar diretórios e subdiretórios recursivamente. Isso é interessante e sempre você acaba precisando; não de um fatorial (que pode ser calculado sem recursão, afinal de contas).

[/quote]

Pode-se sim calcular fatorial sem recursão, mas o intúito foi ajudar e não atrapalhar.
Tentei explicar o conceito…

Sandálias da humildade hein thingol :shock:

É isso aí, Renata, obrigado por dar a explicação completa.

Acho que quando as pessoas não conhecem nada de programação é mais fácil ensinar recursividade que iteratividade (repetição).

É pelos vícios que a gente pega antes de ter programação na faculdade (e pelo método de ensino dos professores) que a recursividade fica imensamente difícil.

Você acaba fazendo de tudo para remover a recursividade de seus programas, e muitas linguagens de programação acabam também não tendo bom suporte à recursividade (por exemplo, o próprio Java tem problemas quando um programa poderia usar “tail recursion” - a JVM não suporta isso, e um programa, que poderia ter 100.000 níveis de recursão se fosse suportada a “tail recursion”, acaba pifando muito antes por “stack overflow”. )

Coincidentemente eu tava estudando recursividade esses dias. Já que você queria saber “onde que são guardados os valores” eu vou copiar uma parte do livro de algoritmos em c++ que eu to lendo.

“In general, most compilers produce code that goes through the same sequence of actions for any procedure call:
‘push the values of local variables and the address of the next instruction on a stack, set the values of parameters to the procedure and
goto the beggining of the procedure’. Then when a procedure completes, it must ‘pop the return address and values of local variables from the stack, reset the values and goto the return address’.”

um exemplo da recursividade tirada mecanicamente (à mão) nesse fibonacci é interessante:

int fibonacci(int n) { //tirei a recursividade usando dois while's //um pra empilhar os valores a serem calculados //e o outro pra desempilhar e calculá-los Pilha *pilha = new Pilha; while (n > 1) { if (n == 1 || n == 0) pilha->empilha(1); else { int tmp = (n - 1) + (n - 2); pilha->empilha(tmp); n -= 2; } } int r = 0; while (!pilha->vazia()) { int x; pilha->topo(x); pilha->desempilha(); r += x; } delete pilha; return r; }

[quote=davidbuzatto]Pq vc não usa um exemplo mais simples para tentar entender?
Por exemplo, como calcular o fatorial de um número usando recursão?

Vc conhece a definição de fatorial?
O fatorial é um valor que é obtido pela multiplicação de um dado número por cada um dos seus antecessores até o 1.
Por exemplo, o fatorial de 3 (denotado como 3! - lê-se “três fatorial”) é:
3 * 2 * 1 = 6

Fatorial de 4 (4!)
4 * 3 * 2 * 1 = 24

Sendo que o fatorial de 0 (0!) por definição é 1.

Entendido o fatorial, vamos para a recursão…

A recursão é obtida quando um método chama a sí próprio n vezes para resolver um problema.
Esse problema deve ter um caso básico, que é o ponto onde o método irá parar de chamar a sí próprio.
No problema do fatorial, qual seria o caso básico?

O caso básico é quando o valor a ser multiplicado for 0, concorda?

Sendo assim, olha o código

[code]
public static long fatorial( n ) {

// caso básico, quando encontrado, retorna o valor do caso 

// básico e não chama mais o método
if ( n == 0 ) {

    return 1;

} else { // caso não seja zero, retorna n + fatorial( n - 1 )

    // n valor atual, n - 1 anterior
    return n * fatorial( n - 1 );

}

}[/code]

Teste de mesa para 3!

fatorial( 3 ) - 3 não é caso básico, chama fatorial de novo
3 * fatorial( 3 - 1 ) - 2 não é caso básico, chama fatorial de novo
2 * fatorial( 2 - 1 ) - 1 não é o caso básico, chama fatorial de novo
1 * fatorial( 1 - 1 ) - 0 é o caso básico, retorna 1

Agora leia de trás para a frente a execução
( 0 )
retorna 1
( 1 )
retorna 1 * 1 = 1
( 2 )
retorna 2 * 1 = 2
( 3 )
retorna 3 * 2 = 6 = 3!

E assim por diante.

Fico umais fácil agora?

Até mais![/quote]
Obrigado!

Consegui entender um pouco mais, mas não consigo fazer o teste de mesa do desse prgrama:

public class FibonacciCalculator {
// declaração recursiva do método fibonacci
public long fibonacci( long number ) {

  if ( ( number == 0 ) || ( number == 1 ) ) {// casos básicos
     return number;

}

else { // passo da recursão
return fibonacci( number - 1) + fibonacci( number - 2 );
}

} // fim do método fibonacci

public void displayFibonacci() {

  for ( int counter = 0; counter <= 3; counter++ ) {
     System.out.println( "Fibonacci of %d is: %d\n" + counter  +
       fibonacci( counter ));

}

} // fim do método displayFibonacci
} // fim da classe FibonacciCalculator

Poderia me explicar por favor ? Obrigado pela paciência.

Entendo seu ponto de vista, mas tudo que pode ser feito recursivamente, pode ser feito iterativamente, como a listagem de diretorios, basta usar uma pilha. Tambem concordo que uma busca em profundidade é mais util e didatico

[quote=Paulo Silveira][quote=thingol]
Por exemplo, algo que as pessoas sempre precisam é de listar diretórios e subdiretórios recursivamente. Isso é interessante e sempre você acaba precisando; não de um fatorial (que pode ser calculado sem recursão, afinal de contas).
[/quote]

Entendo seu ponto de vista, mas tudo que pode ser feito recursivamente, pode ser feito iterativamente, como a listagem de diretorios, basta usar uma pilha. Tambem concordo que uma busca em profundidade é mais util e didatico[/quote]

Na minha opiniao é até mais fácil fazer iterativamente.
Usar um busca em profundidade não tem uma complexidade maior do que usar uma por largura? Digo… mesmo eu buscando qualquer diretório, a complexidade vai ser sempre O(n) (n = quantidade de diretórios). Em largura não seria O(log n)?

Abraço.

Recursão não significa apenas que o método se chama a si mesmo. Muito menos no mesmo escopo.
Isso é apenas um caso particular. Recursão acontece quando um algoritmo se usa a si próprio.
Esse uso pode ser direto ou indireto, no mesmo escopo ou não.

Aí galera, também estou com dúvidas em relação a recursividade. Se alguém pudesse transformar esse método com recursividade eu agradeceria muito e acho que tiraria a dúvida de muitas pessoas. Fiquem na paz!

public void clear() {
for(int i = 0; i < size; i++)
elements[i] = null;
size = 0;
}