Funciona de forma parecida a um laço, se você pensar no aspecto repetitivo de uma chamada recursiva. Repetitivo pois uma função (ou método) recursivo chama a si mesma n vezes, até que uma dada condição seja satisfeita e a função (ou método) retorne.
O consumo de memória realmente é um pouco maior, pois as chamadas são empilhadas e liberadas da memória somente quando a condição trivial (quando a função pára de chamar a si mesma) é alcançada e todas as chamadas retornam.
Outra forma de preencher o vetor usando recursividade.
A idéia é a seguinte:
Se o vetor tiver apenas 1 elemento, atribua um valor a esse elemento.
Para preencher um vetor, você precisa preencher suas duas metades.
class PreencherVetorRecursivo {
/**
* @param vec O vetor a ser preenchido
* @param start A posição inicial a ser preenchida
* @param end A posição final a ser preenchida MAIS UM (como o método String.substring)
* @param val O valor que deve ser usado para preencher o vetor.
*/
public static void preencher (int[] vec, int start, int end, int val) {
if (start >= end) return; // 0 elementos, ou então chamada inválida
if (start + 1 == end) { // ou seja, só um elemento
vec[start] = val;
} else {
// Preencher a primeira metade
preencher (vec, start, (start + end) / 2, val);
// Preencher a segunda metade
preencher (vec, (start + end) / 2, end, val);
}
}
public static void main(String[] args) {
int[] valores = new int[1234];
final int THE_ANSWER = 42;
// Efetuando o preenchimento recursivo...
preencher (valores, 0, valores.length, THE_ANSWER);
// Aqui estamos checando para ver se todos os valores são realmente 42
// - não se esqueça que em Java, ao alocar um vetor os valores são 0 ou null.
boolean ok = true;
for (int i = 0; i < valores.length; ++i) {
if (valores [i] != THE_ANSWER) {
ok = false;
break;
}
}
if (!ok) {
System.out.println ("Não conseguiu preencher o vetor adequadamente");
} else {
System.out.println ("O vetor foi adequadamente preenchido.");
}
}
}
Estou dando esse exemplo de “quebrar o vetor no meio” porque ele se parece um pouco com o quicksort, que também funciona de forma parecida.
Muitos problemas recursivos se resolvem quebrando o problema em pedaços, até que o problema seja suficientemente pequeno para ser resolvido de maneira simples.
No exemplo que lhe passei, eu quebrei o problema em 2, e cada subproblema em 2, até que o problema fosse bem pequeninho (preencher apenas 1 posição do vetor).
Pra você ter noção do quanto um algoritmo recursivo consome mais memória que um não-recursivo, e já que vc quer um exercício, faça algo assim, pra brincar e testar:
Cria aí uma classe que exiba na tela os elementos 1, 2, 3 … 10, 15, 20, 25, 30, 40 de uma Fibonacci. Só que nesse seu programa, crie dois métodos distintos para buscar o n-ésimo elemento da série de Fibonacci, um recursivo e outro não recursivo. No main da sua classe, faça o programa então exibir algo assim na tela:
Existem maneiras e maneiras de fazer a recursividade. Nem sempre a recursividade usa assim mais memória.
O jeito que o Cassio lhe passou “explode” em Java quando o número de elementos do vetor é por volta de 90.000; o meu jeito (quebrando o problema sempre em “metades”) “explode” bem mais devagar. Na prática, meu jeito não deve explodir a pilha (stack) do Java - isso porque o maior tamanho de um vetor em Java são 2^31 elementos e com esse valor uso apenas 32 níveis de recursão, em vez de 2^31 níveis de recursão.
Nem sempre métodos recursivos são tão lentos quanto se pensa. É questão de saber algumas técnicas.
A que vou mostrar agora é a tal de “memoizing”. Rode este programa e você vai ver que Fibonacci não é tão lento assim recursivamente.
import java.util.*;
/**
* "Memoizing" (que eu vou traduzir por "memorização") é uma técnica de acelerar programas
* recursivos guardando resultados já calculados.
* Embora existam maneiras mais simples e eficientes de calcular os números de Fibonacci, vou
* mostrar uma técnica genérica que não exige mexer muito no seu algoritmo.
* Como vocês devem saber:
* fibonacci (0) = 0
* fibonacci (1) = 1
* fibonacci (n) = fibonacci (n - 1) + fibonacci (n - 2)
*/
class FibonacciComMemorizacao {
Map<Integer,Integer> preCalculado = new TreeMap<Integer,Integer>();
public FibonacciComMemorizacao() {
preCalculado.put (0, 0); // fibonacci (0) = 0
preCalculado.put (1, 1); // fibonacci (1) = 1
}
public int fibonacci (int n) {
if (n == 0) {
return 0;
} else if (n == 1) {
return 1;
} else if (n > 1) { // fibonacci (n) = fibonacci (n - 1) + fibonacci (n - 2)
Integer f = preCalculado.get (n); // vamos ver se já foi calculado antes...
if (f != null) {
// volto o valor pré-calculado!
return f;
} else {
// Não tem jeito, tem de calcular pela fórmula recursiva
f = fibonacci (n - 1) + fibonacci (n - 2);
// E agora vamos guardar o resultado
preCalculado.put (n, f);
return f;
}
} else {
throw new IllegalArgumentException ("Definido apenas para os números positivos.");
}
}
public static void main(String[] args) {
FibonacciComMemorizacao fcm = new FibonacciComMemorizacao();
Integer[] valores = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30, 35, 40
};
/*
Valores esperados: (veja a seqüência http://www.research.att.com/~njas/sequences/A000045)
Fibonacci (1) = 1
Fibonacci (2) = 1
Fibonacci (3) = 2
Fibonacci (4) = 3
Fibonacci (5) = 5
Fibonacci (6) = 8
Fibonacci (7) = 13
Fibonacci (8) = 21
Fibonacci (9) = 34
Fibonacci (10) = 55
Fibonacci (15) = 610
Fibonacci (20) = 6765
Fibonacci (25) = 75025
Fibonacci (30) = 832040
Fibonacci (35) = 9227465
Fibonacci (40) = 102334155
*/
for (int i = 0; i < valores.length; ++i) {
System.out.println ("Fibonacci (" + valores[i] + ") = " + fcm.fibonacci (valores[i]));
}
}
}
Eu usei, no “memoizing”, um Map<Integer,Integer> em vez de um int[] (como você também poderia fazer) para exemplificar uma técnica genérica.
É que nem sempre os valores recursivos são assim tão contíguos, como é o caso do Fibonacci.
Existe algum forma de fazer a pratica que vc sugere de uma forma mais elegante como por exemplo a tecnica de divisão, já que eu acredito que a de memorização não vai rolar para este caso.
Fazendo na força bruta, da forma que o cassio indicou eu fiz numa boa, so que com mais elegancia eu não consegui, pois não tive nenhuma boa idéia de algoritmo.