Sei sulla pagina 1di 12

RECURSIVIDAD Notas de clase: borrador

2 ASI - DFSI

Notas de clase: borrador Recursividad


Debemos recordar que un subprograma puede llamar a otro subprgrama, y este a su vez a un tercero. Se dice que un subprograma (mdulo) es recursivo cuando se llama a si mismo. Concepto de recursividad Una funcin recursiva es aquella que se llama a s misma. En C las funciones pueden ser definidas de modo recursivo. La recursividad es una alternativa a la iteracin o repeticin, y aunque en tiempo de computadora y en ocupacin en memoria es la solucin recursiva menos eficiente que la solucin iterativa, existen numerosas situaciones en las que la recursividad es una solucin simple y natural a un problema que en caso contrario ser difcil de resolver. Por esta razn se puede decir que la recursividad es una herramienta potente y til en la resolucin de problemas que tengan naturaleza recursiva o en aquellos que necesiten de estructuras de datos de esta ndole (por ejemplo, listas, rboles y grafos).

Por lo tanto, la tcnica de recursividad se aplicar a problemas que pueden definirse de modo natural de forma recursiva, por ejemplo, la funcin factorial. El factorial de un nmero de define como: Fact(n): 1 si n=0 n * Fact(n-1) si n>0 Como se puede obervar, en la propia especificacin de la funcin se hace uso de la definicin de dicha funcin.

En este tipo de definiciones recursivas ES IMPRESCINDIBLE que exista lo que se denomina CASO BASE: aquella condicin que permite la terminacin de la propia especificacin y devuelve el control a la rutina que lo llam, evitando que se entre en un bucle infinito.

Por lo tanto.. La definicin de un subprograma recursivo consta de dos partes: 1. Ley de recurrencia. 2. Condicin de salida o caso base. La ley de recurrencia del subprograma debe realizar una llamada al mismo subprograma, teniendo en cuenta que el valor de los parmetros debe cambiar en cada llamada. El caso base (condicin de salida o parada) es el caso ms simple conocido y es el que determinar el fin de la recursividad.

Ies Fco. Romero Vargas

Borrador notas de clase

t_recursividad.doc

RECURSIVIDAD Notas de clase: borrador

2 ASI - DFSI

No todos los problemas admiten soluciones recursivas, y si la admiten muchas veces no son inmediatas. Tenga en cuenta que aunque el cdigo de una funcin recursiva pueda ser ms corto o compacto, puede que al ejecutarse sea ms lento y consuma ms memoria, debido a que en cada llamada que el mdulo se hace a s mismo es necesario guardar el contenido de las variables y parmetros que utiliza, y este nmero se incrementar a medida que el nmero de llamadas aumente. En cada llamada a memoria se reserva espacio en memoria para los parmetros formales y las variables locales.

Ejemplos/ejercicios : Factorial de un nmero


/*

*/ #include <stdio.h> int factorial_rec (int n); int factorial_ite (int n); int main() { int num; printf("Introduzca un numero entero positivo "); scanf("%d",&num); printf("\nEl factorial de %d es %d\n",num,factorial_rec(num)); return 0; } int factorial_rec(int n) { int result; if (n <= 0) // if (n<2) result=1; else result= n * factorial_rec(n-1); return result; } int factorial_ite(int n) { int i; int fact=1; for (i=2; i<=n ; i++) fact *= i; return fact; }

Ies Fco. Romero Vargas

Borrador notas de clase

t_recursividad.doc

RECURSIVIDAD Notas de clase: borrador /* con double.. double factorial_ite(double n) { double i; double fact=1.0; for (i=2.0; i<=n ; i++) fact *= i; return fact; } */

2 ASI - DFSI

No siempre es fcil ni posible pasar de una forma a otra. Adems es frecuente mezclar las dos soluciones: intentan hacerlo de forma recursiva con un bucle ! Se debe destacar adems que el programa principal no cambia, aunque la funcin factorial es distinta => respeta el prototipo. Atencin: Problema de rangos con enteros

Ms ejercicios/ejemplos
Potencia /* ** Ejemplo de funcin potencia con recursividad */
#include <stdio.h> double potencia (double, int); main() { double x; int n; printf(" Teclee valor : "); scanf("%lf", &x); printf(" Teclee potencia : "); scanf("%d", &n); printf("Potencia %d de %f es : %f \n", n, x, potencia(x,n)); } double potencia (double x, int n) { double local; if (n==0) local = 1; else local = (x * potencia(x, n-1)); return local; }

Ies Fco. Romero Vargas

Borrador notas de clase

t_recursividad.doc

RECURSIVIDAD Notas de clase: borrador

2 ASI - DFSI

Se propone la realizacin de este ejercicio usando posteriormente el depurador:

Puntos de ruptura Imprimir variables y direcciones de variables y argumentos Imprimir con formato (printf) Uso de commands Display Backtrace. Moverse por la pila (frames) Paso de parmetros por valor y por referencia Uso de gdb y emacs

Divisin entera
Enunciado: Mdulo que devuelve la divisin entera de sus parmetros a y b (supuestos a y b enteros a>=0, b>0). #include <stdio.h> int div_entera(int a, int b, int *resto); main() { int dividendo, divisor, resto, resultado; printf("Introducir dividendo \n"); scanf("%d", &dividendo); printf("Introducir divisor \n"); scanf("%d", &divisor); resultado = div_entera(dividendo, divisor, &resto); printf("La divisin entera de %d y %d es %d", dividendo, divisor, resultado); printf("\n el resto es %d", resto); }

int div_entera(int a, int b, int *resto) { int coc; if(a < b) { *resto = a; coc = 0; } else coc = 1 + div_entera(a-b, b, resto); return coc; }

Ies Fco. Romero Vargas

Borrador notas de clase

t_recursividad.doc

RECURSIVIDAD Notas de clase: borrador

2 ASI - DFSI

Paso de decimal a binario


Enunciado: Mdulo Recursivo que recibe como parmetro un nmero decimal entero mayor que 0 e imprima su equivalente en binario. #include <stdio.h> void dec_bin(int x); int main() { int a; printf("Introducir numero \n"); scanf("%d",&a); dec_bin(a); return 0; }

void dec_bin(int x) { int coc, resto; coc =x/2; resto = x%2; if(coc > 1) dec_bin(coc); else if (coc==1) printf("%d ",1); printf("%d ",resto); }

Ejercicio propuesto: modificar anterior para pasar de decimal a culaquier base el nmero y la base sean parmetros en la lnea de comandos. Si base es >10.. hay que imprimir letras..

Ejemplo: Serie de Fibonnaci.


Consiste en una serie tal que cada trmino se obtiene como suma de los dos anteriores y cuyos dos primeros trminos son 1 y 1..... 1, 1, 2, 3, 5, 8, 13, 21, 34, ....
La funcin se define como:

F(0) = 0 F(1) = 1 F(x) = F(x-1) + F(x-2)

Ies Fco. Romero Vargas

Borrador notas de clase

t_recursividad.doc

RECURSIVIDAD Notas de clase: borrador

2 ASI - DFSI

int fibo (int n) { int r; if (n==0) r = 0; else if (n==1) r = 1; else r = fibo(n-1) + fibo(n-2); return (r); }

Nota profesor:

Ies Fco. Romero Vargas

Borrador notas de clase

t_recursividad.doc

RECURSIVIDAD Notas de clase: borrador

2 ASI - DFSI

Torres de Hanoi Las Torres de Hanoi son un conocido juego de nios, que se juega con tres pivotes y un cierto nmero de discos de diferentes tamaos. Los discos tienen un agujero en el centro, con lo que se pueden apilar en cualquiera de los pivotes. Inicialmente, los discos se amontonan en el pivote de la izquierda por tamao decreciente, es decir, el ms grande abajo y el ms pequeo arriba como se ilustra en la siguiente figura:

Izquierda

Centro

Derecha

El objeto del juego es conseguir lleva los discos del pivote ms a la izquierda al de ms a la derecha, sin colocar nunca un disco sobre otro ms pequeo. Slo se puede mover un disco cada vez, y cada disco debe encontrarse siempre en uno de los pivotes.
Hay 3 varillas, en la primera de ellas existen n discos concntricos,
q q q q

Cada uno de los discos tiene un tamao Inicialmente las otras dos varillas estn vacas. Consiste en mover todos los discos de una varilla a otra. Restriccin: Un disco no puede situarse encima de otro si el tamao del disco que hay debajo es menor que el tamao del disco de arriba.

// Algoritmo recursivo para resolucin // problemas de torre de Hanoi // Aqulas varilla son del tipo char, y se pasan las tres varillas como parmetros #include <iostream.h> void mover(int discos, char origen, char destino, char aux); void grua(char origen, char destino); void main() { int discos; cout << "Introduzca el numero de discos: "; cin >> discos; // mover(discos, 'A', 'B', 'C'); mover(discos, '1', '2', '3'); } //******************************************************* void mover(int discos, char origen, char destino, char aux) { Ies Fco. Romero Vargas Borrador notas de clase t_recursividad.doc

RECURSIVIDAD Notas de clase: borrador

2 ASI - DFSI

if (discos==1) grua(origen,destino); else { mover(discos-1, origen, aux, destino); // mover(1, origen, destino, aux); grua(origen,destino); mover(discos-1, aux, destino, origen); } } //******************************************************* void grua(char origen, char destino) { cout << "Paso disco de " << origen << " a " << destino << "\n"; } /* Ejecucin con discos=4 Paso disco de 1 a 3 Paso disco de 1 a 2 Paso disco de 3 a 2 Paso disco de 1 a 3 Paso disco de 2 a 1 Paso disco de 2 a 3 Paso disco de 1 a 3 Paso disco de 1 a 2 Paso disco de 3 a 2 Paso disco de 3 a 1 Paso disco de 2 a 1 Paso disco de 3 a 2 Paso disco de 1 a 3 Paso disco de 1 a 2 Paso disco de 3 a 2 */

/* En esta segunda solucin se utiliza que la suma de las varillas, representadas por los enteros 1, 2 y 3 suman 6, de forma que dadas dos varillas siempre podemos calcular la tercera => v3 = 6-v 1-v2. Con esto se ahorra el paso de un parmetro a la funcin torres */ */

#include <stdio.h> void Torres(int n, int i, int j); main() { int i, h, f; printf("Escribir el numero de discos \n"); scanf("%d",&i); printf("Escribir el numero de la varilla origen \n"); scanf("%d",&h); printf("Escribir el numero de la varilla destino \n"); scanf("%d",&f); Torres( i, h, f); } void Torres(int n, int i, int j) { int k; if (n == 1) printf("Mueve un disco de la varilla %d a la %d\n", i, j); Ies Fco. Romero Vargas Borrador notas de clase t_recursividad.doc

RECURSIVIDAD Notas de clase: borrador else { k = 6 - i - j Torres( n-1 , Torres( 1 , Torres( n-1 , } }

2 ASI - DFSI

; i , k ); i , j ); k , j );

Ies Fco. Romero Vargas

Borrador notas de clase

t_recursividad.doc

RECURSIVIDAD Notas de clase: borrador

2 ASI - DFSI

10

Laberinto
Se propone realizar la solucin a la salida de un laberinto de forma recursiva. El enunciado es parte de las prcticas.

Ies Fco. Romero Vargas

Borrador notas de clase

t_recursividad.doc

RECURSIVIDAD Notas de clase: borrador

2 ASI - DFSI

11

Comentarios de recursividad tomados de apuntes Fdo. Dominguez Tipos de recursividad.


Haremos las clasificaciones segn dos criterios distintos: Segn el nmero de llamadas recursivas: Lineal. Cuando dentro del subprograma slo hay una llamada recursiva. Binaria. Cuando dentro del subprograma hay dos llamadas recursivas. N-aria. Cuando dentro del subprograma hay N llamadas recursivas.

Atendiendo al nmero de subprogramas enlazados: Directa. Cuando el subprograma se llama a s mismo. Indirecta. Cuando un subprograma A llama al B, y el B llama al A, y as sucesivamente hasta que se cumple una determinada condicin.

Ventajas y desventajas.
La eficacia de un algoritmo viene medida por una serie de factores, entre los que destacan: Espacio en memoria ocupado por el algoritmo. Desde el punto de vista del espacio de memoria ocupado, los algoritmos recursivos ocupan mayor espacio (en la pila o stack) que los iterativos con el inconveniente que puede suponer en el caso de poca memoria disponible: Cada vez que se realiza una llamada a una funcin se guardan en la pila una serie de valores: variables locales, direccin de retorno, parmetros valor y valor devuelto por la funcin. A veces puede suceder que haya tal nmero de llamadas recursivas sin liberar que tengamos un desbordamiento de la pila. Tiempo de ejecucin. Por otro lado, los algoritmos recursivos son ms lentos que los iterativos: Se pierde mucho tiempo en hacer las llamadas recursivas y tener que almacenar los parmetros, la direccin de retorno y las variables locales. Incluso, en ocasiones un algoritmo recursivo se puede volver un mtodo ineficiente ya que realiza muchos ms clculos (muchos de ellos innecesarios) que su equivalente solucin repetitiva. Legibilidad y facilidad de comprensin. Sin embargo, en cierto tipo de problemas, la recursividad conduce a soluciones que son mucho ms fciles de leer y comprender que su correspondiente solucin iterativa; en estos casos la ganancia en claridad puede compensar con creces el coste en tiempo y en memoria de la ejecuc in de programas recursivos. Las definiciones recursivas se caracterizan por su elegancia y simplicidad en contraste con la dificultad y falta de claridad de las definiciones repetitivas. La solucin recursiva es estticamente ms agradable, y una vez acostumbrado, la forma recursiva es normalmente ms fcil de leer y comprender que la forma iterativa. Algunos programadores utilizan la recursividad como una herramienta conceptual: una vez que han escrito la forma recursiva, buscan la forma de convertirla en una versin iterativa y as ganar en eficiencia, en tiempo de ejecucin y en ocupacin de memoria.

Consejos.
No utilice nunca la recursividad, a menos que tenga un medio para terminar las llamadas recursivas. El programador debe tener la seguridad de que en los datos utilizados existe al menos uno que cumpla la condicin de terminacin. Si un proceso se puede definir en trminos ms simples en funcin del mismo, como es el caso de sumas, factoriales, etc., se debe considerar la recursividad.

Ies Fco. Romero Vargas

Borrador notas de clase

t_recursividad.doc

RECURSIVIDAD Notas de clase: borrador

2 ASI - DFSI

12

El nmero de instrucciones (y el diseo) de un programa se puede reducir considerablemente utilizando un proceso recursivo. Existen problemas en los cuales la solucin recursiva es la idnea: por ejemplo, el problema de las Torres de Hanoi o el recorrido de rboles. Las funciones recursivas se pueden utilizar cuando los datos del problema estn organizados en una estructura de datos que se define recursivamente. Si un algoritmo no recursivo se puede obtener y es menos complejo que el recursivo, es normalmente mejor utilizar la versin no recursiva. Cuando se escriben funciones recursivas, se deben verificar siempre para comprobar que no produce recursin infinita. El problema ms frecuente con un procedimiento/funcin recursiva es que puede no terminar adecuadamente. Por ejemplo, si la condicin de terminacin no es correcta o incompleta, entonces el subprograma puede llamarse a s mismo indefinidamente o bien hasta que se gaste toda la memoria disponible. Un error en tiempo de ejecucin (stack overflow) indica que no se ha terminado el procedimiento recursivo. Asegurse de que cada posible caso de recursin tiene una condicin de terminacin.

Ies Fco. Romero Vargas

Borrador notas de clase

t_recursividad.doc

Potrebbero piacerti anche