Es difícil delimitar con precisión las compensaciones relacionadas con la recursividad.
En un nivel matemáticamente abstracto, la recursión proporciona un marco potente para describir el comportamiento explícito de una función. Por ejemplo, podemos definir matemáticamente factorial como
x! = 1 if x == 0
x! = x * (x - 1)! else
O podríamos definir una función más compleja de forma recursiva, por ejemplo, cómo podemos calcular "N elegir K":
C(n, k) = 1 if k == 0
C(n, k) = 0 if k < 0 or if n > k
C(n, k) = C(n - 1, k) + C(n - 1, k - 1) else
Cuando se utiliza la recursividad como una técnica de implementación, no hay garantía de que terminará usando más memoria o produciendo código que se ejecuta de manera más eficiente. A menudo, la recursividad usa más espacio debido a la memoria requerida para contener los marcos de la pila, pero en algunos idiomas esto no es un problema ya que el compilador puede intentar optimizar las llamadas a las funciones (ver, por ejemplo, eliminación de llamadas). En otros casos, la recursividad puede agotar enormes recursos hasta el punto en que el código recursivo no puede terminar en problemas simples.
En cuanto a cuestiones de eficiencia, a menudo el código recursivo es sustancialmente menos eficiente que el código iterativo. Las llamadas a funciones son costosas, y la traducción ingenua de la recursión al código conduce a la duplicación innecesaria del trabajo. Por ejemplo, la aplicación ingenua de Fibonacci
int Fibonacci(int n) {
if (n <= 1) return n;
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
Es terriblemente ineficiente y es tan lento que nunca ha utilizado en la práctica. Aunque el código es más limpio, la ineficiencia borra cualquier beneficio potencial de la recursión.
En otros casos, sin embargo, la recursividad puede ser un increíble ahorro de tiempo.A modo de ejemplo, mergesort es un algoritmo de ordenación muy rápido definido por una hermosa recursividad:
Mergesort(array, low, high) {
if (low >= high - 1) return;
Mergesort(array, low, low + (high - low)/2);
Mergesort(array, low + (high - low)/2, high);
Merge(array, low, low + (high - low)/2, high);
}
Este código es extremadamente rápido y el código iterativo que corresponde probablemente sería más lento, más difícil de leer, y más difícil de entender.
Por lo tanto, en resumen, la recursividad no es una cura mágica ni una fuerza a evitar. Ayuda a iluminar la estructura de muchos problemas que de otra manera podrían parecer difíciles o casi imposibles. Si bien a menudo conduce a un código más claro, a menudo lo hace a expensas del tiempo y la memoria (aunque no necesariamente es automáticamente menos eficiente, sino que puede ser más eficiente en muchos casos). Definitivamente vale la pena estudiar para mejorar el pensamiento algorítmico general y las habilidades para resolver problemas, incluso si nunca escribes otra función recursiva en tu vida.
Espero que esto ayude!
+1 Desearía que más respuestas en SO estuvieran tan bien pensadas como esta. Me gustaría +2 si pudiera. – Josh