Se dice que un
método es recursivo cuando se llama a sí mismo
public
int factorial(int n) { return 1; return n * factorial(n-1); |
factorial( 5 ) = 5 * factorial( 4 ) = 5 * ( 4 * factorial( 3 ) ) = 5 * ( 4 * (3 * factorial( 2 ) ) ) = 5 * ( 4 * (3 * (2 * factorial( 1 ) ) ) ) = 5 * ( 4 * (3 * (2 * ( 1 * factorial( 0 ) ) ) ) ) = 5 * ( 4 * (3 * (2 * ( 1 * 1 ) ) ) ) = 5 * 4 * 3 * 2 * 1 * 1 = 120 |
Este tipo de
programas funciona porque cada método tiene sus propias variables locales
(argumentos). En cada llamada se crea una variable propia y diferente de otras.
Los métodos
recursivos se prestan a errores de programación si el creador no se asegura de
que en ejecución el número de llamadas termina alguna vez. El código anterior
podría estar mal en pequeños detalles que darían lugar a una recursión sin fin:
public
int factorial(int n) { return 1; // modificamos la copia local, pasando el mismo n return n * factorial(n--); |
public
int factorial(int n) { return 1; return n * factorial(n-1); |
Si erramos en la
condición de parada y el método se llama recursivamente sin fin, agotaremos la
memoria disponible para ejecución y java lanzará una excepción
StackOverflowError.
Es prácticamente
obligatorio que los métodos recursivos incluyan código condicional. De hecho,
en su diseño hay que tener en cuanta dos criterios:
1.
en
cada llamada recursiva debemos acercarnos a la solución: convergencia
2.
debe
haber un caso “cero”: terminación
Los métodos
recursivos reflejan en programación la técnica de demostración por inducción de
las matemáticas.
Los programas
recursivos tienen cierta fama, no siempre merecida, de lentos. Aunque conviene
medir tiempos antes de opinar, es cierto que en ocasiones la solución recursiva
es muy elegante pero discutiblemente eficiente.
Existe una forma de plantear métodos recursivos que frecuentemente ayuda a mejorar el tiempo de ejecución. Se conoce como “tail recursion” y consiste en evitar que un método tenga que hacer algo tras conocer el resultado de la llamada recursiva; es decir, que lo último que haga un método sea llamarse recursivamente. El método anterior usando esta técnica quedaría así
public
int factorial(int n) { return factorial2(n, 1); } private
int factorial2(int n, int resultado) { return resultado;
return factorial2(n-1,
resultado*n); |
No obstante, si
es necesario evitar la recursión, siempre puede pasarse a una estructura de
bucles que refleje el mismo razonamiento recursivo:
public
int factorial(int n) { int resultado = 1; while (n >= 1) { resultado*= n; n-= 1; } return
resultado; |
Temas relacionados
115. Método [method] (concepto)