Está en la página 1de 8

Recursión

Algoritmos y
Estructura de
Datos I

1
Recursión
Como vimos en el Módulo 1, un problema complejo puede ser resuelto en
subproblemas más pequeños. En general, en programación se subdivide un
problema en funciones más específicas (y más simples), hasta donde se
logre un nivel de simplicidad que no justifique una subdivisión mayor.

Pero existen problemas donde encontramos que, al subdividirlo en


problemas más pequeños y sencillos, dichos subproblemas son parecidos y
podríamos decir que el problema se puede resolver a través de sí mismo,
mediante funciones que se llamen a sí mismas para ser resueltas. Eso es
conocido como recursión.

Recursión
Si bien estos problemas pueden ser resueltos mediante el uso de la
recursión, también pueden ser resueltos a través de procesos iterativos. En
general, todo algoritmo recursivo puede ser planteado como un algoritmo
iterativo.

Por lo que, ante problemas que son intrínsecamente recursivos, es


importante analizarlos correctamente, para ver si realmente será lo mejor
usar la recursión. Esto es debido a que la recursión no siempre es la mejor
opción para resolver un problema. Si bien es potente, consume muchos
recursos.

Hablamos de procesos o definiciones recursivas cuando dichos procesos o


definiciones se realizan en función de sí mismas. Estos procesos pueden ser
problemas matemáticos, estructuras de datos, entre muchas otras más.

Una función es recursiva cuando, en su desarrollo, se invoca, directa o


indirectamente, a sí misma.

Este proceso de autoinvocación se realiza sucesivamente, donde, cada vez,


el problema a resolver y el nuevo contexto son más sencillos.

La recursión es una potente herramienta de resolución de problemas. La


manera más fácil de expresar muchos algoritmos es mediante formulación
recursiva. Además, las soluciones más eficientes a muchos problemas están
basadas en esta formulación recursiva natural. Pero debemos tener

2
cuidado de no crear una lógica circular que termine haciéndonos entrar en
un bucle infinito. (Weiss, 2013, p. 288).

Demostración matemática
Demostraremos un teorema haciendo uso de la inducción matemática.

La inducción matemática es un razonamiento, basado en el axioma


denominado principio de inducción matemática, que se usa para realizar
demostraciones de proposiciones o teoremas que dependen de una variable
que asume distintos valores positivos.

Cabe aclarar que algunos teoremas o proposiciones pueden ser


demostrados con otros métodos, además de la inducción matemática.

Teorema 1

El teorema plantea lo siguiente:

“Para cualquier entero N≥1, la suma de los N primeros enteros, dada por:
𝑁(𝑁+1)
∑𝑛𝑖=1 𝑖 = 1 + 2 + ⋯ + 𝑁 𝑒𝑠 𝑖𝑔𝑢𝑎𝑙 𝑎 .
2

Comprobemos que este teorema es cierto. Si N=1:

𝑁(𝑁+1) 1(1+1)
∑𝑛𝑖=1 𝑖 = 1 𝑒𝑠 𝑖𝑔𝑢𝑎𝑙 𝑎 = . " (Weiss, 2013, p. 289).
2 2

Evidentemente, para N=1 hemos comprobado que el teorema es cierto.

Ahora bien, si hiciéramos este mismo proceso para 2≤N≤10, también


encontraremos que el teorema es cierto. Sin embargo, manualmente
podremos comprobar cierta cantidad de valores de N, pero no nos
permitirá garantizar que el teorema sea cierto para todos los valores de N.

Las demostraciones por inducción conllevan, principalmente, dos pasos:

1) Primero, se demuestra que el teorema es válido para el primer valor que


cumpla con el teorema. Este paso se suele nombrar como base. Se suele
tomar un valor inicial pequeño, de fácil cálculo manual.
2) Luego, si la base es cierta, entonces se asume como hipótesis inductiva
que el siguiente valor también lo es. Esto significa que si el teorema es

3
cierto para 1≤N≤K, entonces, debería ser cierto también para 1≤N≤K+1.
Este paso se lo suele nombrar como paso inductivo.

Sabremos que se cumple el teorema para cualquier valor de N, si


consideramos que se podrá ampliar indefinidamente su valor, al valor
siguiente según lo definido en el paso inductivo.

Su demostración, entonces, será:

Evidentemente, el teorema es cierto para N=1. Suponga que el teorema es


cierto para todo 1≤N≤K. Entonces:

𝑘+1 𝑘

∑ 𝑖 = (𝑘 + 1) + ∑ 𝑖
𝑖=1 𝑖=1

Por hipótesis, el teorema es cierto para K, así que podemos sustituir el


sumatorio del lado derecho de la ecuación anterior por K(K+1)/2,
obteniendo:

𝑘+1
𝐾(𝐾 + 1)
∑ 𝑖 = (𝑘 + 1) +
2
𝑖=1

Una manipulación algebraica del lado derecho de la ecuación anterior nos


da:

𝑘+1
𝐾+2
∑ 𝑖 = (𝑘 + 1) +
2
𝑖=1

Este resultado confirma el teorema para el caso K+1. Por tanto, por
inducción, el teorema es cierto para todos los enteros N≥1. (Weiss, 2013, p.
289).

Podremos interpretarlo como que, si el teorema es cierto para N=1,


entonces, será cierto para el siguiente valor de N, N=2. Como es cierto para
N=2, será cierto para N=3 y, así, sucesivamente. De este modo, se
demostrará que el teorema es cierto para cualquier valor entero positivo
de N, a partir del valor N=1.

4
Recursión básica

“Las demostraciones por inducción nos muestran que si sabemos que una
afirmación es cierta para el caso más pequeño y podemos demostrar que
un caso implica al siguiente, entonces sabemos que la afirmación es cierta
para todos los casos” (Weiss, 2013, p. 291).

Suele suceder que algunas funciones pueden definirse matemáticamente


de manera recursiva. Este es el caso de la función S(N) = S(N-1)+N, una
función que realiza la suma de los N primeros enteros, como el caso
ejemplificado anteriormente. Sabemos que S(1)=1, de manera que queda
corroborada la instancia más pequeña. Podemos ver que la función S(N)
recursiva es, prácticamente, idéntica a la forma explícita donde S(N) =
N(N+1)/2. La diferencia es que la forma recursiva está solamente definida
para valores enteros y positivos de N.

En pseudocódigo, podríamos resolver la función S(N) de la siguiente


manera:

sumaDePrimerosEnteros(N) {
Existen problemas en
los que su solución
//si n es igual a 1
programática es mejor si (n == 1) {
plantearla de manera //el método termina retornando el valor 1, que corresponde
recursiva, porque a la base S(1)=1
quedan más claros y retornar 1
son más fáciles de
entender. Sin
}
embargo, esto debe //si n es distinto de 1
analizarse sino {
cuidadosamente para //el método termina retornando la forma recursiva
garantizar que no sea retornar s(N-1)+N
a expensas de un gran
deterioro en la
}
administración de los }
recursos.
Como vemos, la función anterior calcula el valor de S(N) de manera
recursiva y resulta muy sencillo de interpretar. Este método funciona
correctamente.

Si, por ejemplo, calculáramos S(4), se inicia el método con N=4. El


condicional no se verifica, por lo que se procede a ejecutar S(3). El
condicional, nuevamente, no se verifica para N=3; se procede a calcular
S(2). El condicional no se verifica para N=2; se procede a calcular S(1). El
condicional se verifica, por lo que el método retornará el valor 1. A partir
de aquí, se continúa con S(2), el cual retorna el valor 3:

5
S(2)=S(N-1)+N=S(2-1)+2=S(1)+2=1+2=3

Luego, se podrá terminar de calcular S(3) de la forma siguiente:

S(3)=S(N-1)+N=S(3-1)+3=S(2)+3

Pero ya sabemos que S(2)=3, por lo que:

S(3)=3+3=6

El método retornará el valor 6. Finalmente, se calcula el valor de S(4)


determinado por la siguiente expresión:

S(4)=S(N-1)+N)=S(4-1)+4=S(3)+4

Si sabemos que S(3)=6, entonces:

S(4)=6+4=10

La dinámica de ejecución de este método, al ser recursivo, implica que se


hagan llamadas sucesivas al método dentro de este. Estos se van apilando
y quedan pendientes hasta que se puedan ir resolviendo uno a uno.

Generalmente, se implementan estas llamadas recursivas en pilas, como


las que fueron explicadas en el módulo anterior. Las pilas de llamadas
almacenan el estado del proceso: valores de las variables, parámetros,
etcétera. Por lo que estas pilas pueden contener cierta cantidad de
llamadas a los métodos, pero no puede ser una carga excesiva para el
procesador de la computadora. Se debería garantizar que el número
máximo de llamadas recursivas será finito y, además, pequeño. De esta
manera, nos aseguraríamos de no superar el nivel máximo de memoria
disponible para almacenar el estado del proceso, en cada momento en que
se abandona, para iniciarse el siguiente. Este estado del proceso es
recuperado cuando se finalizan los procesos siguientes.

Si volvemos al ejemplo planteado anteriormente, observamos que existe


un caso base para el cual definimos que S(1)=1. En un caso base, se define
una instancia a la que se puede resolver, sin la necesidad de usar recursión.
Esto es de gran importancia, debido a que es la manera de garantizar un
corte a nuestro proceso recursivo, para que no se ejecute infinitamente y
desborde la pila de llamadas. Por esto, es evidente concluir que debe
existir un proceso de progresión al caso base, es decir, que los sucesivos
problemas recursivos que se van planteando deben ir convergiendo al caso
base.

6
Podemos encontrar otras aplicaciones de la recursión, no solo el
desarrollado en esta lectura. Así, por ejemplo, podemos resolver
recursivamente los siguientes algoritmos: cálculo factorial, función de
Fibonacci, recorrido de una lista, búsqueda binaria, permutación de
caracteres de una cadena, etcétera.

7
Referencias
Sznajdleder, P. A. (2012). Recursividad. En D. Fernandez (Ed.), Algoritmos a fondo
con implementación en C y JAVA (pp. 463-486). Buenos Aires, AR: Alfaomega.

Weiss, M. A. (2013). Recursión. En M. Martín-Romo (Ed.), Estructuras de datos en


Java (pp. 287-305). Madrid, ES: Pearson.

También podría gustarte