Está en la página 1de 8

Recursividad

Se dice que algo es recursivo si se define en funcin de s mismo o a s mismo. Tambin


se dice que nunca se debe incluir la misma palabra en la definicin de sta. El caso es
que las definiciones recursivas aparecen con frecuencia en matemticas, e incluso en la
vida real. Un ejemplo: basta con apuntar una cmara al monitor que muestra la imagen
que muestra esa cmara. El efecto es verdaderamente curioso, en especial cuando se
mueve la cmara alrededor del monitor.
En matemticas, tenemos mltiples definiciones recursivas:
- Nmeros naturales:
(1) 1 es nmero natural.
(2) el siguiente nmero de un nmero natural es un nmero natural
- El factorial: n!, de un nmero natural (incluido el 0):
(1) si n = 0 entonces: 0! = 1
(2) si n > 0 entonces: n! = n (n-1)!
Asimismo, puede definirse un programa en trminos recursivos, como una serie de
pasos bsicos, o paso base (tambin conocido como condicin de parada), y un paso
recursivo, donde vuelve a llamarse al programa. En un computador, esta serie de
pasos recursivos debe ser finita, terminando con un paso base. Es decir, a cada paso
recursivo se reduce el nmero de pasos que hay que dar para terminar, llegando un
momento en el que no se verifica la condicin de paso a la recursividad. Ni el paso base
ni el paso recursivo son necesariamente nicos.
Por otra parte, la recursividad tambin puede ser indirecta, si tenemos un
procedimiento P que llama a otro Q y ste a su vez llama a P. Tambin en estos casos
debe haber una condicin de parada.
Existen ciertas estructuras cuya definicin es recursiva, tales como los rboles, y los
algoritmos que utilizan rboles suelen ser en general recursivos.
Un ejemplo de programa recursivo en C, el factorial:
int factorial(int n)
{
if (n == 0) return 1;
return n * factorial(n-1);
}
Como se observa, en cada llamada recursiva se reduce el valor de n, llegando el caso en
el que n es 0 y no efecta ms llamadas recursivas. Hay que apuntar que el factorial
puede obtenerse con facilidad sin necesidad de emplear funciones recursivas, es ms, el

uso del programa anterior es muy ineficiente, pero es un ejemplo muy claro.
A continuacin se expone un ejemplo de programa que utiliza recursin indirecta, y nos
dice si un nmero es par o impar. Al igual que el programa anterior, hay otro mtodo
mucho ms sencillo de determinar si un nmero es par o impar, basta con determinar el
resto de la divisin entre dos. Por ejemplo: si hacemos par(2) devuelve 1 (cierto). Si
hacemos impar(4) devuelve 0 (falso).
/* declaracion de funciones, para evitar errores */
int par(int n);
int impar(int n);
int par(int n)
{
if (n == 0) return 1;
return impar(n-1);
}
int impar(int n)
{
if (n == 0) return 0;
return par(n-1);
}
En Pascal se hace as (notar el uso de forward):
function impar(n : Integer) : Boolean; forward;
function par(n : Integer) : Boolean; forward;
function par(n : Integer) : Boolean;
begin
if n = 0 then par := true
else par := impar(n-1)
end;
function impar(n : Integer) : Boolean;
begin
if n = 0 then impar := false
else impar := par(n-1)
end;

Ejemplo: si hacemos la llamada impar(3) hace las siguientes llamadas:


par(2)
impar(1)
par(0) -> devuelve 1 (cierto)

Por lo tanto 3 es un nmero impar.

Qu pasa si se hace una llamada recursiva que no termina?


Cada llamada recursiva almacena los parmetros que se pasaron al procedimiento, y
otras variables necesarias para el correcto funcionamiento del programa. Por tanto si se
produce una llamada recursiva infinita, esto es, que no termina nunca, llega un
momento en el que no quedar memoria para almacenar ms datos, y en ese momento
se abortar la ejecucin del programa. Para probar esto se puede intentar hacer esta
llamada en el programa factorial definido anteriormente:
factorial(-1);
Por supuesto no hay que pasar parmetros a una funcin que estn fuera de su
dominio, pues el factorial est definido solamente para nmeros naturales, pero es un
ejemplo claro.

Cundo utilizar la recursin?


Para empezar, algunos lenguajes de programacin no admiten el uso de recursividad,
como por ejemplo el ensamblador o el FORTRAN. Es obvio que en ese caso se requerir
una solucin no recursiva (iterativa). Tampoco se debe utilizar cuando la solucin
iterativa sea clara a simple vista. Sin embargo, en otros casos, obtener una solucin
iterativa es mucho ms complicado que una solucin recursiva, y es entonces cuando se
puede plantear la duda de si merece la pena transformar la solucin recursiva en otra
iterativa. Posteriormente se explicar como eliminar la recursin, y se basa en
almacenar en una pila los valores de las variables locales que haya para un
procedimiento en cada llamada recursiva. Esto reduce la claridad del programa. An as,
hay que considerar que el compilador transformar la solucin recursiva en una
iterativa, utilizando una pila, para cuando compile al cdigo del computador.
Por otra parte, casi todos los algoritmos basados en los esquemas de vuelta atrs y
divide y vencers son recursivos, pues de alguna manera parece mucho ms natural
una solucin recursiva.
Aunque parezca mentira, es en general mucho ms sencillo escribir un programa
recursivo que su equivalente iterativo. Si el lector no se lo cree, posiblemente se deba a
que no domine todava la recursividad. Se propondrn diversos ejemplos de programas
recursivos de diversa complejidad para acostumbrarse a la recursin.

Ejercicio
La famosa sucesin de Fibonacci puede definirse en trminos de recurrencia de la

siguiente manera:
(1) Fib(1) = 1 ; Fib(0) = 0
(2) Fib(n) = Fib(n-1) + Fib(n-2) si n >= 2
Cuantas llamadas recursivas se producen para Fib(6)?. Codificar un programa que
calcule Fib(n) de forma iterativa.
Nota: no utilizar estructuras de datos, puesto que no queremos almacenar los nmeros
de Fibonacci anteriores a n; s se permiten variables auxiliares.

Ejemplos de programas recursivos


- Dados dos nmeros a (nmero entero) y b (nmero natural mayor o igual que cero)
determinar a^b.
int potencia(int a, int b)
{
if (b == 0) return 1;
else return a * potencia(a, b-1);
}
La condicin de parada se cumple cuando el exponente es cero. Por ejemplo, la
evaluacin de potencia(-2, 3) es:
potencia(-2, 3) ->
(-2) potencia(-2, 2) ->
(-2) (-2) potencia(-2, 1) ->
(-2) (-2) (-2) potencia(-2, 0) ->
(-2) (-2) (-2) 1
y a la vuelta de la recursin se tiene:
(-2) (-2) (-2) 1 /=/ (-2) (-2) (-2) potencia(-2,0)
< (-2) (-2) (-2) /=/ (-2) (-2) potencia(-2, 1)
< (-2) 4 /=/ (-2) potencia(-2,2)
< -8 /=/ potencia(-2,3)
en negrita se ha resaltado la parte de la expresin que se evala en cada llamada
recursiva.

- Dado un array constituido de nmeros enteros y que contiene N elementos siendo N


>= 1, devolver la suma de todos los elementos.
int sumarray(int numeros[], int posicion, int N)
{
if (posicion == N-1) return numeros[posicion];
else return numeros[posicion] + sumarray(numeros, posicion+1, N);

}
...
int numeros[5] = {2,0,-1,1,3};
int N = 5;
printf("%d\n",sumarray(numeros, 0, N));
Notar que la condicin de parada se cumple cuando se llega al final del array. Otra
alternativa es recorrer el array desde el final hasta el principio (de derecha a izquierda):
int sumarray(int numeros[], int posicion)
{
if (posicion == 0) return numeros[posicion];
else return numeros[posicion] + sumarray(numeros, posicion-1);
}
...
int numeros[5] = {2,0,-1,1,3};
int N = 5;
printf("%d\n",sumarray(numeros, N-1));

- Dado un array constituido de nmeros enteros, devolver la suma de todos los


elementos. En este caso se desconoce el nmero de elementos. En cualquier caso se
garantiza que el ltimo elemento del array es -1, nmero que no aparecer en ninguna
otra posicin.
int sumarray(int numeros[], int posicion)
{
if (numeros[posicion] == -1) return 0;
else return numeros[posicion] + sumarray(numeros, posicion+1);
}
...
int numeros[5] = {2,4,1,-3,-1};
printf("%d\n",sumarray(numeros, 0));

La razn por la que se incluye este ejemplo se debe a que en general no se conocer el
nmero de elementos de la estructura de datos sobre la que se trabaja. En ese caso se
introduce un centinela -como la constante -1 de este ejemplo o la constante NULO para
punteros, u otros valores como el mayor o menor entero que la mquina pueda
representar- para indicar el fin de la estructura.

- Dado un array constituido de nmeros enteros y que contiene N elementos siendo N


>= 1, devolver el elemento mayor.
int mayor(int numeros[], int posicion)
{
int aux;
if (posicion == 0)
return numeros[posicion];

else {
aux = mayor(numeros, posicion-1);
if (numeros[posicion] > aux)
return numeros[posicion];
else
return aux;
}

}
...
int numeros[5] = {2,4,1,-3,-1};
int N = 5;
printf("%d\n", mayor(numeros, 4));

- Ahora uno un poco ms complicado: dados dos arrays de nmeros enteros A y B de


longitud n y m respectivamente, siendo n >= m, determinar si B est contenido en A.
Ejemplo:
A = {2,3,4,5,6,7,-3}
B = {7,-3} -> contenido; B = {5,7} -> no contenido; B = {3,2} -> no contenido
Para resolverlo, se parte del primer elemento de A y se compara a partir de ah con
todos los elementos de B hasta llegar al final de B o encontrar una diferencia.
A = {2,3,4,5}, B = {3,4}
-2,3,4,5
3,4
^
En el caso de encontrar una diferencia se desplaza al segundo elemento de A y as
sucesivamente hasta demostrar que B es igual a un subarray de A o que B tiene una
longitud mayor que el subarray de A.
3,4,5
3,4
Visto de forma grfica consiste en deslizar B a lo largo de A y comprobar que en alguna
posicin B se suporpone sobre A.
Se han escrito dos funciones para resolverlo, contenido y esSubarray. La primera
devuelve cierto si el subarray A y el array B son iguales; tiene dos condiciones de
parada: o que se haya recorrido B completo o que no coincidan dos elementos. La
segunda funcin es la principal, y su cometido es ir 'deslizando' B a lo largo de A, y en
cada paso recursivo llamar una vez a la funcin contenido; tiene dos condiciones de
parada: que el array B sea mayor que el subarray A o que B est contenido en un
subarray A.
int contenido(int A[], int B[], int m, int pos, int desp)
{
if (pos == m) return 1;
else if (A[desp+pos] == B[pos])
return contenido(A,B,m, pos+1, desp);
else
return 0;
}

int esSubarray(int A[], int B[], int n, int m, int desp)


{
if (desp+m > n)
return 0;
else if (contenido(A, B, m, 0, desp))
return 1;
else
return esSubarray(A, B, n, m, desp+1);
}
...
int A[4] = {2, 3, 4, 5};
int B[3] = {3, 4, 5};
if (esSubarray(A, B, 4, 5, 0)) printf("\nB esta contenido en A");
else printf("\nB no esta contenido en A");
Hay que observar que el requisito n >= m indicando en el enunciado es innecesario, si
m > n entonces devolver falso nada ms entrar en la ejecucin de esSubarray.
Este algoritmo permite hacer bsquedas de palabras en textos. Sin embargo existen
algoritmos mejores como el de Knuth-Morris-Prat, el de Rabin-Karp o mediante
autmatas finitos; estos algoritmos som ms complicados pero mucho ms efectivos.

- Dado un array constituido de nmeros enteros y que contiene N elementos siendo N


>= 1, devolver el elemento mayor. En este caso escribir un procedimiento, es decir,
que el elemento mayor devuelto sea una variable que se pasa por referencia.
void mayor(int numeros[], int posicion, int *m)
{
if (posicion == 0)
*m = numeros[posicion];
else {
mayor(numeros, posicion-1, m);
if (numeros[posicion] > *m)
*m = numeros[posicion];
}
}
...
int numeros[5] = {2,4,1,-3,-1};
int M;
mayor(numeros, 5-1, &M);
printf("%d\n", M);

Hay que tener cuidado con dos errores muy comunes: el primero es declarar la variable
para que se pase por valor y no por referencia, con lo cual no se obtiene nada. El otro
error consiste en llamar a la funcin pasando en lugar del parmetro por referencia una
constante, por ejemplo: mayor(numeros, 5-1, 0); en este caso adems se producir un
error de compilacin.

- La funcin de Ackermann, siendo n y m nmeros naturales, se define de la siguiente

manera:
Ackermann(0, n) = n + 1
Ackermann(m, 0) = A(m-1, 1)
Ackermann(m, n) = A(m-1, A(m, n-1))
Aunque parezca mentira, siempre se llega al caso base y la funcin termina. Probar a
ejecutar esta funcin con diversos valores de n y m... que no sean muy grandes!. En
Internet pueden encontrarse algunas cosas curiosas sobre esta funcin y sus
aplicaciones.

Ejercicios propuestos
Nota: para resolver los ejercicios basta con hacer un nico recorrido sobre el array.
Tampoco debe utilizarse ningn array auxiliar, pero si se podrn utilizar variables de tipo
entero o booleano.
- Dado un array constituido de nmeros enteros y que contiene N elementos siendo N
>= 1, escribir una funcin que devuelva la suma de todos los elementos mayores que
el ltimo elemento del array.
- Dado un array constituido de nmeros enteros y que contiene N elementos siendo N
>= 1, escribir una funcin que devuelva cierto si la suma de la primera mitad de los
enteros del array es igual a la suma de la segunda mitad de los enteros del array.
- Dados dos arrays A y B de longitud n y m respectivamente, n >= m cuyos elementos
estn ordenados y no se repiten, determinar si todos los elementos de B estn
contenidos en A. Recordar que los elementos estn ordenados, de esta manera basta
con realizar un nico recorrido sobre cada array.

Conclusiones
En esta seccin se ha pretendido mostrar que la recursividad es una herramienta
potente para resolver mltiples problemas. Es ms, todo programa iterativo puede
realizarse empleando expresiones recursivas y viceversa.

También podría gustarte