COMPARTE ESTE ARTÍCULO

Una función recursiva es aquella que se llama a sí misma (directa o indirectamente) para resolver un problema dividiéndolo en subproblemas más pequeños del mismo tipo. Cada llamada reduce el problema hasta llegar a una condición de parada (caso base) que se resuelve sin más recursión. Cuando cada llamada termina, la pila de ejecuciones se va desapilando y los resultados parciales se combinan.


Anatomía de la recursión

  1. Caso base
    – Condición que detiene la recursión.
    – Debe alcanzarse siempre, o la función se ejecutará indefinidamente.
  2. Paso recursivo
    – La función se invoca con una entrada más pequeña o “más cercana” al caso base.
    – El problema se descompone y se delega la parte restante a la misma función.
  3. Combinación de resultados
    – Las llamadas van devolviendo valores que se combinan (sumas, concatenaciones, etc.) hasta resolver el problema original.

Ejemplo clásico: factorial

public static long Factorial(int n)
{
    if (n < 0) throw new ArgumentException("n debe ser >= 0");
    if (n <= 1)            // 1) caso base
        return 1;

    return n * Factorial(n - 1); // 2) paso recursivo
}
  • Base: n <= 1 devuelve 1.
  • Paso recursivo: multiplica n por Factorial(n-1).

Comparación con iteración

RecursiónIteración
LegibilidadExpresa la lógica de forma más cercana a la definición matemática.Suele ser más verboso.
Uso de memoriaCada llamada ocupa una posición en la pila (overhead).Un único marco de pila; menor riesgo de desbordamiento.
ComplejidadPuede simplificar algoritmos complejos (árboles, grafos).Más adecuada para bucles simples y cálculos grandes.

Use recursión cuando la estructura del problema sea naturalmente recursiva (árboles, división y conquista) y sea más clara que la versión iterativa.


Más ejemplos prácticos

1. Serie de Fibonacci (recursión ingenua)

public static long Fibonacci(int n)
{
    if (n < 0) throw new ArgumentException();
    if (n <= 1) return n;               // base
    return Fibonacci(n - 1) + Fibonacci(n - 2); // recursivo
}

O(n²) y muchas llamadas repetidas. Conviene memorizar o iterar si n es grande.

2. Recorrido de un directorio

public static void ImprimirArchivos(string ruta)
{
    foreach (var archivo in Directory.GetFiles(ruta))
        Console.WriteLine(archivo);

    foreach (var subdir in Directory.GetDirectories(ruta))
        ImprimirArchivos(subdir);   // profundiza un nivel
}

3. Búsqueda binaria recursiva

public static int Buscar(int[] datos, int objetivo, int ini, int fin)
{
    if (ini > fin) return -1;                  // no encontrado

    int medio = (ini + fin) / 2;

    if (datos[medio] == objetivo) return medio;
    if (objetivo < datos[medio])
        return Buscar(datos, objetivo, ini, medio - 1);
    else
        return Buscar(datos, objetivo, medio + 1, fin);
}

Recursión de cola (tail recursion)

En otros lenguajes, los compiladores pueden optimizar la recursión de cola eliminando marcos de pila intermedios. En C# todavía no hay TCO (Tail Call Optimization) garantizada, así que no confíes en ella para evitar desbordamientos; en su lugar:

  • Reescribe en forma iterativa si n puede ser muy grande.
  • O bien usa un Stack manual u otras estructuras.

Buenas prácticas al usar recursión en C#

  1. Define un caso base claro y alcanzable.
  2. Controla la profundidad máxima. StackOverflowException no se puede capturar.
  3. Evita cálculos duplicados. Usa memoization o una versión iterativa si el problema tiene subproblemas superpuestos (p. ej., Fibonacci).
  4. Prefiere parámetros inmutables o copias; evita efectos laterales inesperados.
  5. Documenta la función: indica complejidad temporal y espacial.
  6. Unit tests: cubre casos límite (0, 1, máximos).

Errores comunes

  • Olvidar el caso base ⇒ recursión infinita.
  • Modificar variables globales dentro de cada llamada ⇒ produce resultados imprevistos.
  • No validar argumentos negativos o nulos antes de recursar.
  • Suponer optimización de cola ⇒ puede fallar en producción bajo carga.

Conclusión

Las funciones recursivas son potentes para expresar algoritmos que se descomponen naturalmente en subproblemas (por ejemplo, estructuras jerárquicas, búsquedas divide-y-vencerás). En C#, la recursión se implementa de forma sencilla, pero es crucial manejar de forma segura la pila y evitar cálculos redundantes. Con un caso base sólido, un paso recursivo correcto y pruebas adecuadas, la recursión puede hacer tu código más claro, elegante y fácil de mantener.

Contenido restringido

Acceso de usuarios existentes
   
Registro de un nuevo usuario
*Campo necesario

Categories:

Tags:

Comments are closed

Estado de acceso
ESTADO DE ACCESO
TRADUCTORES
COMPARTENOS
error: CONTENIDO PROTEGIDO