You are on page 1of 11

Repblica Bolivariana de Venezuela Ministerio Del Poder Popular Para La Defensa Universidad Nacional Experimental Politcnica De La Fuerza Armada

UNEFA Yaracuy Nirgua

Recursividad

Alumno: Jos A. Silva Clisanchez CI. 22317008 Ing. De Sistemas 7mo Semestre Noviembre 2013

Recursividad.
La recursividad es una tcnica de programacin que se utiliza para realizar una llamada a una funcin desde ella misma, de all su nombre. El ejemplo ms utilizado por su fcil comprensin es el clculo de nmeros factoriales. El factorial de 0 es por definicin 1. Los factoriales de nmeros mayores se calculan mediante la multiplicacin de 1 * 2 *... incrementando el nmero de 1 en 1 hasta llegar al nmero para el que se est calculando el factorial. El siguiente prrafo muestra una funcin, expresada con palabras, que calcula un factorial. "Si el nmero es menor que cero, se rechaza. Si no es un entero, se redondea al siguiente entero. Si el nmero es cero, su factorial es uno. Si el nmero es mayor que cero, se multiplica por l factorial del nmero menor inmediato." Para calcular el factorial de cualquier nmero mayor que cero hay que calcular como mnimo el factorial de otro nmero. La funcin que se utiliza es la funcin en la que se encuentra en estos momentos, esta funcin debe llamarse a s misma para el nmero menor inmediato, para poder ejecutarse en el nmero actual. Esto es un ejemplo de recursividad. Un algoritmo recursivo es un algoritmo que expresa la solucin de un problema en trminos de una llamada a s mismo. La llamada a si mismo se conoce como llamada recursiva o recurrente. #include <iostream> #include <cstdlib> using namespace std; int Factorial(int n); int main(){ int valor; system("clear"); cout << "Introduzca numero a calcular: "; cin >> valor; cout << "\nEl Factorial de " << valor << " es: " << Factorial(valor) << endl; return 0; } int Factorial(int n){ if (n < 0){ cout << No existe el factorial de un numero negativo.\n; }else if(n < 2){ return 1; }else return n * Factorial(n-1); }

Generalmente, si la primera llamada al subprograma se plantea sobre un problema de tamao u orden N, cada nueva ejecucin recurrente del mismo se planteara sobre problemas, de igual naturaleza que el original, pero de un tamao menor que N. De esta forma, al ir reduciendo progresivamente la complejidad del problema a resolver, llegara un momento en que su resolucin sea ms o menos trivial (o al menos suficientemente manejable como para resolverlo de forma no recursiva). En esa situacin diremos que estamos ante un caso base de la recursividad. Es frecuente que los algoritmos recurrentes sean ms ineficientes en tiempo que los iterativos aunque suelen ser mucho ms breves en espacio. La recursividad y la iteracin (ejecucin en bucle) estn muy relacionadas, cualquier accin que pueda realizarse con la recursividad puede realizarse con iteracin y viceversa. Normalmente, un clculo determinado se prestar a una tcnica u otra, slo necesita elegir el enfoque ms natural o con el que se sienta ms cmodo. Claramente, esta tcnica puede constituir un modo de meterse en problemas. Es fcil crear una funcin recursiva que no llegue a devolver nunca un resultado definitivo y no pueda llegar a un punto de finalizacin. Este tipo de recursividad hace que el sistema ejecute lo que se conoce como bucle "infinito"

Recursividad Directa vs Indirecta.


Cuando en una subrutina hay llamadas a ella misma se habla de recursividad directa, en contraposicin, cuando se tienen varias subrutinas y estas se llaman unas a otras formando ciclos se dice que la recursin es indirecta. Subrutina_ A Subrutina_ A Subrutina_ A Subrutina_ A Subrutina_ B Subrutina_ C Subrutina_ D Subrutina_ A

Propiedades de las Definiciones o Algoritmos Recursivos:


Un requisito importante para que sea correcto un algoritmo recursivo es que no genere una secuencia infinita de llamadas as mismo. Claro que cualquier algoritmo que genere tal secuencia no termina nunca. Una funcin recursiva f debe definirse en trminos que no impliquen a f al menos en un argumento o grupo de argumentos. Debe existir una "salida" de la secuencia de llamadas recursivas. Si en esta salida no puede calcularse ninguna funcin recursiva. Cualquier caso de definicin recursiva o invocacin de un algoritmo recursivo tiene que reducirse a la larga a alguna manipulacin de uno o casos ms simples no recursivos. - No debe generar una secuencia infinita de llamadas as mismo, dicho de otro modo ha de existir al menos un caso base.

-Una funcin recursiva f debe definirse en trminos que no impliquen a f al menos en un argumento o grupo de argumentos. -Debe existir una "salida" de la secuencia de llamadas recursivas. -Cada llamada recurrente se debera definir sobre un problema de menor complejidad (algo ms fcil de resolver).

Programacin Recursiva.
Es mucho mas difcil desarrollar una solucin recursiva en un lenguaje determinado para resolver un problema especifico cuando no se tiene un algoritmo. No es solo el programa sino las definiciones originales y los algoritmos los que deben desarrollarse. En general, cuando encaramos la tarea de escribir un programa para resolver un problema no hay razn para buscar una solucin recursiva. La mayora de los problemas pueden resolverse de una manera directa usando mtodos no recursivos, sin embargo, otros pueden resolverse de una manera ms lgica y elegante mediante la recursin. Volviendo a examinar la funcin factorial. El factor es, probablemente, un ejemplo fundamental de un problema que no debe resolverse de manera recursiva, dado que su solucin iterativa es directa y simple. Sin embargo, examinaremos los elementos que permiten dar una solucin recursiva. Antes que nada, puede reconocerse un gran nmero de casos distintos que se deben resolver. Es decir, quiere escribirse un programa para calcular 0!, 1!, 2! Y as sucesivamente. Puede identificarse un caso "trivial" para el cual la solucin no recursiva pueda obtenerse en forma directa. Es el caso de 0!, que se define como 1. El siguiente paso es encontrar un mtodo para resolver un caso complejo en trminos de uno ms simple, lo cual permite la reduccin de un problema complejo a uno ms simple. La transformacin del caso complejo al simple resultara al final en el caso trivial. Esto significara que el caso complejo se define, en lo fundamental en trminos del ms simple.

Examinaremos que significa lo anterior cuando se aplica la funcin factorial. 4! Es un caso ms complejo que 3!, La transformacin que se aplica al nmero a para obtener 3 es sencillamente restar 1. Si restamos 1 de 4 de manera sucesiva llegamos a 0, que es el caso trivial. As, si se puede definir 4! en trminos de 3! y en general n! en trminos de (n 1)!, se podr calcular 4! mediante la definicin de n! en trminos de (n 1)! al trabajar, primero hasta llegar a 0! y luego al regresar a 4! . En el caso de la funcin factorial se tiene una definicin de ese tipo, dado que: n! = n * (n 1)! As, 4! = 4 * 3! = 4 * 3 * 2! = 4 * 3 * 2 * 1! = 4 * 3 * 2 * 1 * 0! = 4 * 3 * 2] * ] = 24

Estos son los ingredientes esenciales de una rutina recursiva, poder definir un caso "complejo" en trminos de uno ms "simple" y tener un caso "trivial" (no recursivo) que pueda resolverse de manera directa. Al hacerlo puede desarrollarse una solucin si se supone que se ha resuelto el caso ms simple la versin C de la funcin factorial supone que est definido (n 1)! y usa esa cantidad al calcular n! Ejemplo de la sucesin de Fibonacci En matemticas, la sucesin de Fibonacci es la siguiente sucesin infinita de nmeros naturales: 0,1,1,2,3,5,8,13,21,34,55,89,144... El primer elemento es 0, el segundo es 1 y cada elemento restante es la suma de los dos anteriores: 0 si i = 0 fi = 1 si i = 1 f(i-2)+f(i-1) si i > 1 A cada elemento de esta sucesin se le llama nmero de Fibonacci. Esta sucesion fue descrita en Europa por Leonardo de Pisa, matemtico italiano del siglo XIII tambin conocido como Fibonacci. El cdigo en C++ que representa la funcin Fibonacci es el siguiente: #include <iostream> #include <cstdlib> using namespace std; int Fibonacci(int n); int main(){ int valor; system("clear"); cout << "Introduzca numero a calcular: "; cin >> valor; cout << "\nEl Fibonacci de " << valor << " es: " << Fibonacci(valor) << endl; return 0; } int Fibonacci(int n){ if (n < 0){ cout << No existe Fibonacci para numeros negativos.; } else if (n == 0) { return 0; } else if (n == 1) { return 1; }else return Fibonacci(n-2) + Fibonacci(n -1); }

Cadenas Recursivas:
Una funcin recursiva no necesita llamarse a s misma de manera directa. En su lugar, puede hacerlo de manera indirecta como en el siguiente ejemplo: a (formal parameters) b (formal parameters) {{ .. b (arguments); a (arguments); .. } /*fin de a*/ } /*fin de b*/ En este ejemplo la funcin a llama a b, la cual puede a su vez llamar a a, que puede llamar de nuevo a b. As, ambas funciones a y b, son recursivas, dado que se llaman a s mismo de manera indirecta. Sin embargo, el que lo sea no es obvio a partir del examen del cuerpo de una de las rutinas en forma individual. La rutina a, parece llamar a otra rutina b y es imposible determinar que se puede llamar as misma de manera indirecta al examinar slo a a. Pueden incluirse ms de dos rutinas en una cadena recursiva. As, una rutina a puede llamar a b, que llama a c, que llama a z, que llama a a. Cada rutina de la cadena puede potencialmente llamarse a s misma y por lo tanto es recursiva. Por supuesto, el programador debe asegurarse de que un programa de este tipo no genere una secuencia infinita de llamadas recursivas.

Definicin recursiva de expresiones algebraicas


Como ejemplo de cadena recursiva consideremos el siguiente grupo de definiciones: a. Una expresin es un trmino seguido por un signo ms seguido por un trmino, o un trmino solo b. Un trmino es un factor seguido por un asterisco seguido por un factor, o un factor solo. c. Un factor es una letra o una expresin encerrada entre parntesis. Antes de ver algunos ejemplos, obsrvese que ninguno de los tres elementos anteriores est definido en forma directa en sus propios trminos. Sin embargo, cada uno de ellos se define de manera indirecta, una expresin se define por medio de un trmino, un trmino por medio de un factor y un factor por medio de una expresin. De manera similar, se define un factor por medio de una expresin, que se define por medio de un trmino que a su vez

se define por medio de un factor. As, el conjunto completo de definiciones forma una cadena recursiva. La forma ms simple de un factor es una letra. As A, B, C, Q, Z y M son factores. Tambin son trminos, dado que un trmino puede ser un factor solo. Tambin son expresiones dado que una expresin puede ser un trmino solo como A es una expresin, (A) es un factor y, por lo tanto, un trmino y una expresin. A+B es un ejemplo de una expresin que no es ni un trmino ni un factor, sin embargo (A+B) es las tres cosas. A*B es un trmino y en consecuencia una expresin, pero no es un factor. A*B+C es una expresin, pero no es un factor. Cada uno de los ejemplos anteriores es una expresin valida. Esto puede mostrarse al aplicar la definicin de una expresin de cada uno. Considrese, sin embargo la cadena A+*B. No es ni una expresin, ni un trmino, ni un factor. Sera instructivo para el lector intentar aplicar la definicin de expresin, trmino y factor para ver que ninguna de ellas describe a la cadena A+*B. De manera similar, (A+B*) C y A+B+C son expresiones nulas de acuerdo con las definiciones precedentes.

Asignacin Esttica y Dinmica de Memoria:


Hasta este momento solamente hemos realizado asignaciones estticas del programa, y ms concretamente estas asignaciones estticas no eran otras que las declaraciones de variables en nuestro programa. Cuando declaramos una variable se reserva la memoria suficiente para contener la informacin que debe almacenar. Esta memoria permanece asignada a la variable hasta que termine la ejecucin del programa (funcin main). Realmente las variables locales de las funciones se crean cuando stas son llamadas pero nosotros no tenemos control sobre esa memoria, el compilador genera el cdigo para esta operacin automticamente. En este sentido las variables locales estn asociadas a asignaciones de memoria dinmicas, puesto que se crean y destruyen durante la ejecucin del programa. As entendemos por asignaciones de memoria dinmica, aquellas que son creadas por nuestro programa mientras se estn ejecutando y que por tanto, cuya gestin debe ser realizada por el programador. El lenguaje C dispone, como ya indicamos con anterioridad, de una serie de libreras de funciones estndar. El fichero de cabeceras stdlib.h contiene las declaraciones de dos funciones que nos permiten reservar memoria, as como otra funcin que nos permite liberarla. Las dos funciones que nos permiten reservar memoria son: *malloc (cantidad_de_memoria); *calloc (nmero_de_elementos, tamao_de_cada_elemento);

Estas dos funciones reservan la memoria especificada y nos devuelven un puntero a la zona en cuestin. Si no se ha podido reservar el tamao de la memoria especificado devuelve un puntero con el valor 0 o NULL. El tipo del puntero es, en principio void, es decir, un puntero a cualquier cosa. Por tanto, a la hora de ejecutar ests funciones es aconsejable realizar una operacin cast (de conversin de tipo) de cara a la utilizacin de la aritmtica de punteros a la que aludamos anteriormente. Los compiladores modernos suelen realizar esta conversin automticamente. Antes de indicar como deben utilizarse las susodichas funciones tenemos que comentar el operador sizeof. Este operador es imprescindible a la hora de realizar programas portables, es decir, programas que puedan ejecutarse en cualquier mquina que disponga de un compilador de C. El operador sizeof (tipo de dato), nos devuelve el tamao que ocupa en memoria un cierto tipo de dato, de esta manera, podemos escribir programas independientes del tamao de los datos y de la longitud de palabra de la mquina. En resumen si no utilizamos este operador en conjuncin con las conversiones de tipo cast probablemente nuestro programa slo funciones en el ordenador sobre el que lo hemos programado. Por ejemplo, en los sistemas PC, la memoria est orientada a bytes y un entero ocupa 2 posiciones de memoria, sin embargo puede que en otro sistema la mquina est orientada a palabras (conjuntos de 2 bytes, aunque en general una mquina orientada a palabras tambin puede acceder a bytes) y por tanto el tamao de un entero sera de 1 posicin de memoria, suponiendo que ambas mquinas definan la misma precisin para este tipo. Ejemplo de un programa que reserva dinmicamente memoria para algn dato. #include <stdlib.h #include <stdio.h> main() { int *p_int; float *mat; p_int = (int *) malloc(sizeof(int)); mat = (float *)calloc(20,sizeof(float)); if ((p_int==NULL)||(mat==NULL)) { printf ("\nNo hay memoria"); exit(1); } /* Aqu iran las operaciones sobre los datos */ /* Aqu ira el cdigo que libera la memoria */ } Este programa declara dos variables que son punteros a un entero y a un float. A estos punteros se le asigna una zona de memoria, para el primero se reserva memoria para almacenar una variable entera y en el segundo se

crea una matriz de veinte elementos cada uno de ellos un float. Obsrvese el uso de los operadores cast para modificar el tipo del puntero devuelto por malloc y calloc, as como la utilizacin del operador sizeof. Como se puede observar no resulta rentable la declaracin de una variable simple (un entero, por ejemplo, como en el programa anterior) dinmicamente, en primer lugar porque aunque la variable slo se utilice en una pequea parte del programa, compensa tener menos memoria (2 bytes para un entero) que incluir todo el cdigo de llamada a malloc y comprobacin de que la asignacin fue correcta (esto seguro que ocupa ms de dos bytes). En segundo lugar tenemos que trabajar con un puntero con lo cual el programa ya aparece un poco ms engorroso puesto que para las lecturas y asignaciones de las variables tenemos que utilizar el operador *. Para terminar un breve comentario sobre las funciones anteriormente descritas. Bsicamente da lo mismo utilizar malloc y calloc para reservar memoria es equivalente: mat = (float *)calloc (20,sizeof(float)); mat = (float *)malloc (20*sizeof(float)); La diferencia fundamental es que, a la hora de definir matrices dinmicas calloc es mucho ms claro y adems inicializa todos los elementos de la matriz a cero. Ntese tambin que puesto que las matrices se referencian como un puntero la asignacin dinmica de una matriz nos permite acceder a sus elementos con instrucciones de la forma: Cabe destacar que en realidad existen algunas diferencias al trabajar sobre mquinas con alineamiento de palabras. mat[0] = 5; mat[2] = mat[1]*mat[6]/67; Con lo cual el comentario sobre lo engorroso que resultaba trabajar con un puntero a una variable simple, en el caso de las matrices dinmicas no existe diferencia alguna con una declaracin normal de matrices. La funcin que nos permite liberar la memoria asignada con malloc y calloc es free(puntero), donde puntero es el puntero devuelto por malloc o calloc. En nuestro ejemplo anterior, podemos ahora escribir el cdigo etiquetado como: /* Ahora ira el cdigo que libera la memoria */ free (p_int); free(mat); Hay que tener cuidado a la hora de liberar la memoria. Tenemos que liberar todos los bloques que hemos asignado, con lo cual siempre debemos

tener almacenados los punteros al principio de la zona que reservamos. Si mientras actuamos sobre los datos modificamos el valor del puntero al inicio de la zona reservada, la funcin free probablemente no podr liberar el bloque de memoria.

Ejemplos de Programas De Recursividad


Programa en C++ que calcula el producto de dos nmeros de forma recursiva. Los nmeros a multiplicar se leen por teclado. #include <iostream> using namespace std; int producto(int, int); int main() { int n1,n2,p; cout << "Introduzca primer numero: "; cin >> n1; cout << "Introduzca segundo numero: "; cin >> n2; p=producto(n1,n2); cout << "producto: " << p << endl; system("pause"); } int producto(int a, int b) { if(a==0 or b==0) return 0; else { return a+producto(a,b-1); } }

Programa en C++ que Calcula el mximo comn divisor de dos nmeros de forma recursiva. #include<stdio.h> int MCD(int x, int y) { if(y==0) return x; else

return MCD(y, x%y); } int main() { int num1=0,num2=0; printf("::MAXIMO COMUN DIVISOR::\n"); printf("Introduce el primer numero: ");scanf("%i",&num1); printf("Introduce el segundo numero: ");scanf("%i",&num2); printf("\tEl resultado es: %i\n", MCD(num1, num2)); return 0; } Programa en C++ que permita Convertir Decimal a Binario de forma recursiva #include<stdio.h> void binario(int n) { if (n!=0) { binario(n/2); printf("%i",n%2); } } int main() { int num=0; printf("::CONVERTIR DECIMAL A BINARIO::\n"); printf("Introduce un numero: ");scanf("%i",&num); //Pedir variable num printf("\t");binario(num);printf("\n"); //Llamar la funcin return 0; }

You might also like