Tabla de contenidos
Definir el concepto de recursión (Bloom 1*)
Describir la secuencia de computaciones que se llevan a cabo al evaluar un método recursivo (Bloom 2*)
Convertir un método recursivo por la cola a forma iterativa (Bloom 3*)
Clasificar un método recursivo a uno de los tipos de recursión estudiados (Bloom 3*)
Dado un método recursivo, encontrar el dominio de convergencia para casos sencillos (Bloom 4)
Escribir un método recursivo dado un problema a resolver (Bloom 5)
Comparar diferentes métodos recursivos que resuelven el mismo problema (Bloom 6)
El siguiente código que permite calcular la serie armónica es
erróneo
y provocará una excepción de tipo
StackOverflowError
:
public double h(int n) { return h(n-1) + 1.0/n; }
El método recursivo, aún con el caso base que se muestra, seguirá
provocando una
excepción de tipo
StackOverflowError
:
public double h(int n) { if (n==1) return 1.0; return h(n-1) + 1.0/n; }
La recursión por la cola se puede convertir de forma inmediata en iteración. Cuando existe una llamada recursiva en un método y que es la última instrucción del mismo, el método se puede convertir automáticamente en iterativo. El siguiente método tiene su versión iterativa.
public void torresHanoi(int n,char de,char to,char ch){ if (n==1) System.out.println("Mover 1 de"+de+"hacia"+to); else if (n>1) { torresHanoi(n-1, de, ch, to); System.out.println("Mover "+n+"de"+de+"hacia"+ to); torresHanoi(n-1, ch,to, de); }}
A veces la recursión no es eficiente. Por ejemplo, la serie de los números de
Fibonacci
se puede calcular mediante el siguiente método recursivo. Sin embargo, el tiempo
que tarda dicho método crece de de forma exponencial con n
, lo que hace que
sea inútil el cálculo salvo para valores pequeños de n
.
public static long fib(int n) { if (n <= 1) return n; else return fib(n-1) + fib(n-2); }siendo fib(0) = 0 y fib(1) = 1
Se puede optimizar el anterior algoritmo recursivo no lineal para el cálculo de la serie de Fibonnaci por otro de recursividad lineal que permite hacerlo más eficiente.
En general, si se dispone de un algoritmo recursivo y otro iterativo para resolver un problema, se suele preferir el iterativo por razones de eficiencia.
Un algoritmo de ordenamiento, como puede ser el "QuickSort" de orden logarítmico O(n·log n), siempre es más rápido que otro de orden cuadrático O(n^2) como el "BubbleSort" cuando n se hace muy grande.
Si vamos a trabajar con tamaños de datos pequeños (valores bajos de n), el orden de complejidad del algoritmo recursivo no es significativo y puede, incluso, llegar a ser un problema.
En función del tamaño (n) del conjunto de datos que manipula un algoritmo, aumenta la evolución del coste temporal del mismo en base a los órdenes crecientes de complejidad y de las constantes multiplicativas.
Los algoritmos de Backtracking suelen resolverse de forma recursiva.
Puedes verificar si has programado correctamente un método recursivo contestando a tres preguntas sobre el caso base y los casos recursivos respectivamente.
La tecnica de diseño de algoritmos llamada "divide y vencerás" usando recursividad permite, aplicada al algoritmo de ordenación "QuickSort", conseguir disminuir su tiempo promedio de ejecución hasta ser de orden logarítmico O(log n).
La "programación dinámica" permite resolver sub-problemas generados por un planteamiento recursivo de forma no recursiva, guardando los valores de las soluciones en una tabla.
La complejidad de este fragmento de código es O(n^2)
for (int i = 0; i < n; i++) { for (int k = 0; k < i; i++) { m[i][k] = a[i] * a[k] } }