Volver a la portadaCiencias de la Computación · 2.° año de bachillerato · Cambiar asignatura
Empezar gratis
2° bach · Ciencias de la Computación
Algoritmos avanzados y análisis de datos · Algoritmos avanzados

Recursividad y optimización de algoritmos

Tenés 200 fichas de inscripción de un centro escolar desordenadas sobre una mesa. Ordenarlas una por una contra todas las demás sería interminable. ¿Habrá una forma de que el problema se resuelva «solo», repitiendo la misma idea con montones cada vez más pequeños? Eso es pensar de forma recursiva. Al final de la página volvemos a esas fichas.

Qué es un algoritmo recursivo

Un algoritmo es recursivo cuando, para resolver un problema, se llama a sí mismo con una versión más pequeña del mismo problema. Es como las muñecas rusas: abrís una y adentro hay otra igual, pero más chica, hasta llegar a la más pequeña, que ya no se abre.

Esa muñeca final es la clave: sin ella, el proceso no terminaría nunca. Por eso toda función recursiva necesita dos partes.

El factorial es el ejemplo clásico. $n!$ es el producto de todos los naturales de 1 a $n$, y se define así:

  • Caso base: $0! = 1$

  • Caso recursivo: $n! = n \cdot (n-1)!$ para $n \geq 1$

Cadena de llamadas de 4 factorial: cada llamada espera a la siguiente hasta llegar al caso base 0 factorial igual a 1 4! 3! 2! 1! 0! = 1 4 · 3! 3 · 2! 2 · 1! 1 · 0! Las llamadas bajan hasta el caso base y luego las respuestas suben.
Practica★☆☆Sin empezar
Explicá con tus palabras qué es un algoritmo recursivo. En tu respuesta mencioná cuáles son las dos partes que no pueden faltar en una función recursiva: el caso base y…

Explicá con tus palabras qué es un algoritmo recursivo. En tu respuesta mencioná cuáles son las dos partes que no pueden faltar en una función recursiva: el caso base y el caso recursivo.

Resolver
Practica★☆☆Sin empezar
Observá esta definición del factorial de un número natural $n$: $0! = 1$ $n! = n \cdot (n-1)!$ para $n \geq 1$ Indicá cuál es el caso base y cuál es el caso recursivo.…

Observá esta definición del factorial de un número natural $n$:

  • $0! = 1$

  • $n! = n \cdot (n-1)!$ para $n \geq 1$

Indicá cuál es el caso base y cuál es el caso recursivo. Luego calculá $4!$ siguiendo la definición.

Resolver

Trazar una función recursiva

Trazar es seguir a mano cada llamada y anotar qué devuelve. Se usa pseudocódigo en español: función, si … sino y devolver.

función suma(n):

    si n = 1:

        devolver 1

    sino:

        devolver n + suma(n - 1)

Fijate en los resultados: 1, 3, 6, 10. La función calcula $1 + 2 + 3 + \dots + n$, la suma de los primeros $n$ números naturales. Por qué: cada llamada suma su propio $n$ al total de los anteriores.

Una definición recursiva también sirve para sucesiones. Por ejemplo, 2, 4, 8, 16, 32… cada término es el doble del anterior:

  • Caso base: $T(1) = 2$

  • Caso recursivo: $T(n) = 2 \cdot T(n-1)$ para $n \geq 2$

Practica★★☆Sin empezar
Considerá esta función escrita en pseudocódigo: a) Seguí paso a paso la ejecución de suma(4) y escribí cada llamada y el valor que devuelve. b) Explicá qué calcula esta…

Considerá esta función escrita en pseudocódigo:

función suma(n):

    si n = 1:

        devolver 1

    sino:

        devolver n + suma(n - 1)
  1. a) Seguí paso a paso la ejecución de suma(4) y escribí cada llamada y el valor que devuelve.
  2. b) Explicá qué calcula esta función en general.
Resolver
Practica★★☆Sin empezar
Gabriela observa la sucesión de números: 2, 4, 8, 16, 32, ... a) Describí el patrón que reconocés en la sucesión. b) Escribí una definición recursiva (con caso base y…

Gabriela observa la sucesión de números: 2, 4, 8, 16, 32, ...

  1. a) Describí el patrón que reconocés en la sucesión.
  2. b) Escribí una definición recursiva (con caso base y caso recursivo) que permita obtener el término $n$ de la sucesión.
  3. c) Calculá el sexto término usando tu definición.
Resolver

Dividir y vencer

Dividir y vencer es una estrategia: partís un problema grande en subproblemas más pequeños del mismo tipo, resolvés cada uno y combinás las respuestas. Se lleva muy bien con la recursividad, porque cada parte se resuelve con la misma idea.

Volvamos a las 200 fichas: se reparten en montones de, por ejemplo, 50; se ordena cada montón (más fácil, pues es chico) y luego se juntan los montones ya ordenados. Cada paso trabaja con menos fichas, y juntar montones ordenados es rápido.

Dividir y vencer: 200 fichas se dividen en montones, cada uno se ordena y luego se combinan 200 fichas 100 100 200 ordenadas dividir dividir ordenar combinar
Practica★☆☆Sin empezar
Un estudiante de Ciencias de la Computación tiene que ordenar 200 fichas de inscripción de un centro escolar. Decide dividirlas en montones más pequeños, ordenar cada…

Un estudiante de Ciencias de la Computación tiene que ordenar 200 fichas de inscripción de un centro escolar. Decide dividirlas en montones más pequeños, ordenar cada montón y luego juntarlos. Indicá qué estrategia de diseño de algoritmos está usando y explicá en una o dos frases por qué ayuda a resolver el problema.

Resolver

Eficiencia: contar pasos

Dos algoritmos pueden resolver lo mismo y ser muy distintos. La eficiencia se mide contando cuántos pasos (por ejemplo, comparaciones) necesita el algoritmo, sobre todo cuando los datos son muchos. No se mide en segundos, porque eso depende de la computadora.

Caso: buscar la matrícula 4,350 en una lista de 1,000 estudiantes ordenada de menor a mayor.

  • Búsqueda uno por uno (A): en el peor caso revisa los 1,000 elementos.

  • Búsqueda por mitades (B): compara con el elemento del medio y descarta la mitad que no sirve. Es dividir y vencer.

Con B, la lista se reduce así: 1,000 → 500 → 250 → 125 → … → 1. Cada comparación la parte a la mitad. Como $2^{10} = 1{,}024 > 1{,}000$, bastan unas 10 comparaciones como máximo.

Algoritmo Comparaciones (máximo) con 1,000 datos
A: uno por uno 1,000
B: por mitades alrededor de 10
Practica★★☆Sin empezar
Daniela debe encontrar el número de matrícula 4,350 en una lista de 1,000 estudiantes ordenada de menor a mayor. Propone dos algoritmos: Algoritmo A: revisar uno por uno…

Daniela debe encontrar el número de matrícula 4,350 en una lista de 1,000 estudiantes ordenada de menor a mayor. Propone dos algoritmos:

  • Algoritmo A: revisar uno por uno desde el primero hasta encontrarlo.

  • Algoritmo B: comparar con el elemento del medio y descartar la mitad de la lista que no sirve, repitiendo el proceso.

  1. a) Indicá cuál algoritmo es más eficiente cuando la lista es grande y explicá por qué.
  2. b) Estimá cuántas comparaciones necesita como máximo el algoritmo A y justificá por qué el B necesita muchas menos (te sirve saber que $2^{10} = 1,024$).
Resolver

Recursividad ineficiente y su mejora

Que un algoritmo sea recursivo no garantiza que sea eficiente. El caso típico es Fibonacci:

  • $F(1) = 1$, $F(2) = 1$

  • $F(n) = F(n-1) + F(n-2)$ para $n \geq 3$

Cada llamada genera dos llamadas nuevas. Mirá el árbol para $F(5)$:

Árbol de llamadas de F(5): F(3) aparece dos veces y F(2) tres veces, lo que muestra el trabajo repetido F(5) F(4) F(3) F(3) F(2) F(2) F(1) F(2) F(1)

Observá que $F(3)$ se calcula dos veces y $F(2)$ tres veces. Con $F(6)$ el desperdicio crece: $F(3)$ se calcula 3 veces. Al aumentar $n$, el árbol casi se duplica en cada nivel, y el trabajo explota.

Dos mejoras:

  • Guardar resultados ya calculados (una tabla o lista) y consultarlos antes de recalcular.

  • Usar un ciclo que avance desde $F(1)$ y $F(2)$ guardando solo los dos últimos valores.

Así cada $F(k)$ se calcula una sola vez: el trabajo crece de forma proporcional a $n$.

Torres de Hanói

Hay tres varillas y $n$ discos de distintos tamaños apilados en la primera. Hay que pasarlos a la tercera moviendo un disco por vez y sin poner uno grande sobre uno chico. La solución es recursiva:

  1. Mover $n-1$ discos a la varilla auxiliar.

  2. Mover el disco más grande a la varilla destino.

  3. Mover de nuevo los $n-1$ discos sobre el más grande.

  4. Caso base: con 1 disco, se mueve directamente (1 movimiento).

  5. Caso recursivo: para $n$ discos se resuelve el problema con $n-1$ discos dos veces, más un movimiento.

Si $M(n)$ es el número de movimientos: $M(1) = 1$ y $M(n) = 2\,M(n-1) + 1$.

Cada disco extra duplica (y suma uno) el número de movimientos. Con 10 discos son 1,023 movimientos; con 20, más de un millón. Aunque el algoritmo es correcto y sencillo, el trabajo crece tan rápido que para muchos discos ninguna computadora lo termina en un tiempo razonable.

Practica★★★Sin empezar
La sucesión de Fibonacci se define así: $F(1) = 1$, $F(2) = 1$ $F(n) = F(n-1) + F(n-2)$ para $n \geq 3$ Un estudiante implementa esta definición de forma recursiva…

La sucesión de Fibonacci se define así:

  • $F(1) = 1$, $F(2) = 1$

  • $F(n) = F(n-1) + F(n-2)$ para $n \geq 3$

Un estudiante implementa esta definición de forma recursiva directa para calcular $F(6)$.

  1. a) Dibujá o describí el árbol de llamadas que se genera e indicá cuántas veces se calcula $F(3)$.
  2. b) Explicá por qué este algoritmo es ineficiente cuando $n$ crece.
  3. c) Proponé una mejora (por ejemplo, guardar resultados ya calculados o usar un ciclo) y argumentá por qué reduce el trabajo.
Resolver
Practica★★★Sin empezar
Josué tiene que escribir un algoritmo para el juego de las Torres de Hanói con 3 discos. La solución recursiva consiste en: mover $n-1$ discos a la varilla auxiliar,…

Josué tiene que escribir un algoritmo para el juego de las Torres de Hanói con 3 discos. La solución recursiva consiste en: mover $n-1$ discos a la varilla auxiliar, mover el disco más grande a la varilla destino y mover de nuevo los $n-1$ discos sobre el más grande.

  1. a) Identificá el caso base y el caso recursivo de la estrategia.
  2. b) Si $M(n)$ es el número de movimientos para $n$ discos, planteá la relación $M(n) = 2\,M(n-1) + 1$ con $M(1) = 1$ y calculá $M(3)$ y $M(5)$.
  3. c) Analizá qué ocurre con el número de movimientos al agregar un disco más y explicá qué implica eso para la eficiencia del algoritmo.
Resolver

Volvé al principio: las 200 fichas se ordenan dividiéndolas en montones más pequeños, ordenando cada uno y juntándolos. Eso es dividir y vencer, y se apoya en la misma idea recursiva de achicar el problema hasta que sea trivial.

Resumen

  • Un algoritmo recursivo se llama a sí mismo con un problema más pequeño; necesita caso base (se resuelve directo) y caso recursivo (se acerca al base).

  • Para trazar, anotá cada llamada y esperá al caso base; luego las respuestas suben.

  • Dividir y vencer parte el problema en subproblemas del mismo tipo y combina las soluciones.

  • La eficiencia se compara contando pasos: buscar por mitades necesita unas 10 comparaciones con 1,000 datos ordenados, frente a 1,000 uno por uno.

  • Una recursión puede repetir trabajo (Fibonacci) y se mejora guardando resultados o usando un ciclo; Hanói muestra que algunos algoritmos crecen muy rápido: $M(n) = 2\,M(n-1) + 1$.

Ver todos los ejercicios de este apartado