de la Eficiencia en la Recursividad Autor: Jorge Jimnez Muoz 1A Gestin 2 INDICE: 1. Introduccin al trabajo. 2. Recursividad. i. Introduccin a la recursividad. ii. Conceptos de Recursividad. iii. Propiedades de las definiciones o algoritmos recursivos. iv. Programacin recursiva, v. Diseo de algoritmos recursivos. vi. Recursividad directa e indirecta. vii. Recursividad Vs Iteracin. 3. Programacin Dinmica. i. Introduccin a la programacin dinmica. ii. Concepto de programacin dinmica. iii. Procesos polietapicos de decisin. 4. Conclusiones 5. Bibliografa 3 1. Introduccin al trabajo. El siguiente trabajo trata del anlisis, de la programacin dinmica. Siendo esta una solucin de mejora a la poca eficiencia de la programacin recursiva. La manera de afrontar este trabajo, y explicar en un modo sencillo, que entienda todo el mundo, tras consultar muchas fuentes, es el siguiente, primero veremos a grandes rasgos, en que consiste la programacin recursiva, sus propiedades, definiciones, ventajas e inconvenientes, y todo ello acompaado de ejemplos, y uno en comn junto a la programacin dinmica, de manera que podamos observar el mismo problema desde las dos perspectivas, que es, el calculo de los nmeros de Fibonacci. El por que de hablar extendidamente de la programacin recursiva es, porque esta, junto con algunas tcnicas de programacin como el divide y vencers, algoritmo voraz, o backtracking, la programacin dinmica no es mas que una mejora bastante significativa basada en la programacin recursiva, aprovechando los clculos que generan las llamadas, e ir almacenndolos para su posterior uso, no teniendo que volver a calcularlo, aunque lo veremos con mas extensin mas adelante. 2. Recursividad 2.i. Introduccin a la Recursividad. El rea de la programacin es muy amplia y con muchos detalles. Los programadores necesitan ser capaces de resolver todos los problemas que se les presente a travs del computador aun cuando en el lenguaje que utilizan no haya una manera directa de resolver los problemas. En los lenguajes de programacin, se puede aplicar una tcnica que se le dio el nombre de recursividad por su funcionalidad. Esta tcnica es utilizada en la programacin estructurada para resolver problemas que tengan que ver con el factorial de un nmero, o juegos de lgica. Las asignaciones de memoria pueden ser dinmicas o estticas y hay diferencias entre estas dos y se pueden aplicar las dos en un programa cualquiera. 2.ii Conceptos de Recursividad. La recursividad es una tcnica de programacin importante. Se utiliza para realizar una llamada a una funcin desde la misma funcin. Como ejemplo til se puede presentar el clculo de nmeros factoriales. l 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. 4 "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 me nor 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. 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". Ejemplo: Secuencia de nmeros de Fibonacci. Para entender mejor lo que en realidad es el concepto de recursin veremos un poco lo referente a la secuencia de Fibonacci. Principalmente habra que aclarar que es un ejemplo menos familiar que el del factorial, que consiste en la secuencia de enteros. 0,1,1,2,3,5,8,13,21,34,..., Cada elemento en esta secuencia es la suma de los precedentes (por ejemplo 0 + 1 = 0, 1 + 1 = 2, 1 + 2 = 3, 2 + 3 = 5, ...) sean fib(0) = 0, fib (1) = 1 y as sucesivamente, entonces puede definirse la secuencia de Fibonacci mediante la definicin recursiva (define un objeto en trminos de un caso mas simple de si mismo): fib (n) = n if n = = 0 or n = = 1 fib (n) = fib (n - 2) + fib (n - 1) if n >= 2 Por ejemplo, para calcular fib (6), puede aplicarse la definicin de manera recursiva para obtener: Fib (6) = fib (4) + fib (5) = fib (2) + fib (3) + fib (5) = fib (0) + fib (1) + fib (3) + fib (5) = 0 + 1 fib (3) + fib (5) 1. + fib (1) + fib (2) + fib(5) = 1. + 1 + fib(0) + fib (1) + fib (5) = 2. + 0 + 1 + fib(5) = 3 + fib (3) + fib (4) = 5 3. + fib (1) + fib (2) + fib (4) = 3 + 1 + fib (0) + fib (1) + fib (4) = 4. + 0 + 1 + fib (2) + fib (3) = 5 + fib (0) + fib (1) + fib (3) = 5. + 0 + 1 + fib (1) + fib (2) = 6 + 1 + fib (0) + fib (1) = 6. + 0 + 1 = 8 Obsrvese que la definicin recursiva de los nmeros de Fibonacci difiere de las definiciones recursivas de la funcin factorial y de la multiplicacin . La definicin recursiva de fib se refiere dos veces a s misma . Por ejemplo, fib (6) = fib (4) + fib (5), de tal manera que al calcular fib (6), fib tiene que aplicarse de manera recursiva dos veces. Sin embargo calcular fib (5) tambin implica calcular fib (4), as que al aplicar la definicin hay mucha redundancia de clculo. En ejemplo anterior, fib(3) se calcula tres veces por separado. Sera mucho ms eficiente "recordar" el valor de fib(3) la primera vez que se calcula y volver a usarlo cada vez que se necesite. Es mucho mas eficiente un mtodo iterativo como el que sigue parar calcular fib (n). If (n < = 1) return (n); lofib = 0 ; hifib = 1 ; for (i = 2; i < = n; i ++) { x = lofib ; lofib = hifib ; hifib = x + lofib ; } /* fin del for*/ return (hifib) ; Comprese el numero de adiciones (sin incluir los incrementos de la variable ndice, i) que se ejecutan para calcular fib (6) mediante este algoritmo al usar la definicin recursiva. En el caso de la funcin factorial, tienen que ejecutarse el mismo nmero de multiplicaciones para calcular n! Mediante ambos mtodos: recursivo e iterativo. Lo mismo ocurre con el nmero de sumas en los dos mtodos al calcular la multiplicacin. Sin embargo, en el caso de los nmeros de Fibonacci, el mtodo recursivo es mucho ms costoso que el iterativo. 2.iii. 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 6 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. 2.iv. Programacin Recursiva: Es mucho mas difcil desarrollar una solucin recursiva en un lenguaje para resolver un problema especfico 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 numero 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 esta definido (n 1)! y usa esa cantidad al calcular n!. 7 Otra forma de aplicar estas ideas a otros ejemplos antes explicados. En la definicin de a * b, es trivial el caso de b = 1, pues a * b es igual a a. En general, a + b puede definirse en trminos de a * (b 1) mediante la definicin a * b = a * (b 1) + a. De nuevo, el caso complejo se transforma en un caso mas simple al restar 1, lo que lleva, al final, al caso trivial de b = 1. Aqu la recursin se basa nicamente en el segundo parmetro, b. Con respecto al ejemplo de la funcin de Fibonacci, se definieron dos casos triviales: fib(0) = 0 y fib(1) = 1. Un caso complejo fib(n) se reduce entonces a dos ms simples: fib(n 1) y fib(n 2). Esto se debe a la definicin de fib(n) como fib(n 1) + fib(n 2), donde se requiere de dos casos triviales definidos de manera directa. Fib(1) no puede definirse como fib(0) + fib(-1) porque la funcin de Fibonacci no est definida para nmeros negativos. 2.v. Diseo de algoritmos recursivos. Para resolver un problema, el primer paso ser la identificacin de un algoritmo recursivo, es decir, descomponer el problema en subproblemas de menor tamao (aunque de la misma naturaleza del problema original) y componer la solucin final a partir de las subsoluciones obtenidas. Tendremos que disear: casos base, casos generales y la solucin en trminos de ellos. Casos base: Son los casos del problema que se resuelve con un segmento de cdigos sin recursividad. Siempre debe existir al menos un caso base el numero y forma de los casos base son hasta Cierto punto arbitrario. La solucin ser mejor cuanto mas simple y eficiente resulte el conjunto De casos seleccionados. Casos generales: Si el problema es suficientemente complejo, la solucin se expresa, de forma recursiva, como la unin de 1. La solucin de uno o ms subproblemas (de igual naturaleza pero menor tamao). 2. Un conjunto de pasos adicionales. Estos pasos junto con las soluciones a los subproblemas Componen la solucin al problema general que queremos resolver. Los casos generales siempre deben avanzar hacia un caso base. Es decir, la llamada recursiva se hace a un subproblema ms pequeo y, en ltima instancia, los casos generales alcanzaran un caso base. 8 2.vi. Recursividad directa e indirecta. En recursin directa el cdigo del subprograma recursivo F contiene una sentencia que invoca a F, mientras que en recursin indirecta el subprograma F invoca al subprograma P, y as sucesivamente hasta que se invoca de nuevo al subprograma F Si una funcin, procedimiento o mtodo se invoca a si misma, el proceso se denomina recursin directa; si una funcin, procedimiento o mtodo puede invocar a una segunda funcin, procedimiento o mtodo que a su vez invoca a la primera, este proceso se conoce como recursin indirecta o mutua. Un requisito para que un algoritmo sea correcto es que no genere una secuencia infinita de llamadas sobre si mismo. Cualquier algoritmo que genere una secuencia de este tipo no puede terminar nunca. En consecuencia, la definicin cursiva debe incluir un componente base (condicin de salida) en el que f(n) se define directamente (es decir, no recursivamente) para uno o mas valores de n. Debe existir una <<forma de salir>> de la secuencia de llamadas recursivas. As en la funcin f(n)=n! para n entero. La recursividad indirecta se produce cuando un subprograma llama a otro, que eventualmente terminara llamando de nuevo al primero. 2.vii. Recursividad Vs Iteracin. El las secciones anteriores se han estudiado varias funciones que se puede implementar fcilmente o de modo recursivo o bien de modo iterativo. En esta parte compararemos los dos enfoques y examinaremos las razones por las que el programador puede elegir u enfoque u otro segn la situacin especifica. Tanto la iteracin como la recursin se basan en una estructura de control: la iteracin utiliza una estructura repetitiva y la recursin utiliza una estructura de seleccin. La iteracin y la recursin implican ambas repeticin: la iteracin utiliza explcitamente una estructura repetitiva mientras que la recursin consume la repeticin mediante llamadas repetidas. La iteracin y la recursin implican cada una un test mientras que la recursin termina cuando se reconoce un caso base o la condicin de salida se alcanza. La recursin tiene muchas desventajas. Se invoca repetidamente al mecanismo de recursividad y en consecuencia se necesita tiempo suplementario para realizar las mencionadas llamadas. Esta caracterstica puede resultar cara en tiempo de procesador y espacio de memoria. Cada llamada de una funcin recursiva produce otra copia de la funcin (realmente solo las variables de funcin) sea creada; esto puede consumir memoria considerable. Por el contrario, la iteracin se produce dentro de una funcin, de modo que las operaciones suplementarias de las llamadas a la funcin y asignacin de memoria adicional son omitidas. 9 En consecuencia, Cules son las razones para elegir la recursin? La razn fundamental es que existen numerosos problemas complejos que poseen naturaleza recursiva y, en consecuencia, son ms fciles de implementar con algoritmos de este tipo. Sin embargo, en condiciones crticas de tiempo y de memoria, es decir, cuando el consumo de tiempo y memoria sean decisivos o concluyentes para la resolucin del problema, la solucin a elegir debe ser, normalmente la iterativa. 3. PROGRAMACION DINMICA. 3.i. Introduccin a la programacin dinmica. Existe una serie de problemas cuyas soluciones pueden ser expresadas recursivamente en trminos matemticos, y posiblemente la manera ms natural de resolverlos es mediante un algoritmo recursivo. Sin embargo, el tiempo de ejecucin de la solucin recursiva, normalmente de orden exponencial y por tanto impracticable, puede mejorarse substancialmente mediante la programacin Dinmica. Frecuentemente para resolver un problema complejo se tiende a dividir este en subproblemas, ms pequeos, resolver estos ltimos (recurriendo posiblemente a nuevas subdivisiones) y combinar las soluciones obtenidas para calcular la solucin del problema inicial. Puede ocurrir que la divisin natural del problema conduzca a un gran nmero de subejemplares idnticos. Si se resuelve cada uno de ellos sin tener en cuenta las posibles repeticiones, resulta un algoritmo ineficiente; en cambio si se resuelve cada ejemplar distinto una sola vez y se conserva el resultado, el algoritmo obtenido es mucho mejor. La programacin dinmica es un mtodo ascendente. Se resuelven primero los subejemplares ms pequeos y por tanto ms simples. Combinando las soluciones se obtienen las soluciones de ejemplares sucesivamente ms grandes hasta llegar al ejemplar original. La idea de la tcnica de "dividir y vencer" es llevada al extremo en la programacin dinmica. Si en el proceso de resolucin del problema resolvemos un subproblema, almacenamos la solucin, por si esta puede ser necesaria de nuevo para la resolucin del problema. Estas soluciones parciales de todos los subproblemas se almacenan en una tabla, sin tener en cuenta si va a ser realmente necesarias posteriormente en la solucin total. Con el uso de esta tabla se evitan hacer clculos idnticos reiteradamente, mejorando as la eficiencia en la obtencin de la solucin. Tambin sirve para convertir ciertas funciones recursivas en iterativas. Ejemplo: Clculo de los 100 primeros ns primos, donde almacenamos cada primo encontrado en una tabla y dividimos cada nuevo nmero slo por los que hay en la tabla (y no por todos los menores que l) para saber si es primo: 10 void primos( int n ) { int tabla[N]; int i, j, nprimos = 0; for( i=2; i<=n; i++ ) { j = 0; while( (j<nprimos) && (i % tabla[j]!=0 ) j++; if( j == nprimos ) { tabla[nprimos] = 1; nprimos++; printf("..."); } } } La programacin dinmica se emplea a menudo para resolver problemas de optimizacin que satisfacen el principio de optimalidad: en una secuencia ptima de decisiones toda subsecuencia ha de ser tambin ptima. La Programacin Dinmica no slo tiene sentido aplicarla por razones de eficiencia, sino porque adems presenta un mtodo capaz de resolver de manera eficiente problemas cuya solucin ha sido abordada por otras tcnicas y ha fracasado. Donde tiene mayor aplicacin la Programacin Dinmica es en la resolucin de problemas de optimizacin. En este tipo de problemas se pueden presentar distintas soluciones, cada una con un valor, y lo que se desea es encontrar la solucin de valor ptimo (mximo o mnimo). La solucin de problemas mediante esta tcnica se basa en el llamado principio de ptimo enunciado por Bellman en 1957 y que dice: En una secuencia de decisiones ptima toda subsecuencia ha de ser tambin ptima. Hemos de observar que aunque este principio parece evidente no siempre es aplicable y por tanto es necesario verificar que se cumple para el problema en cuestin. Un ejemplo claro para el que no se verifica este principio aparece al tratar de encontrar el camino de coste mximo entre dos vrtices de un grafo ponderado. 3.ii. Concepto de la Programacin dinmica La estrategia algotrmica conocida como programacin dinmica es una tcnica de resolucin que encuentra aplicacin en numerosos problemas de optimizacin. El tipo de problemas abordables es, principalmente, aquel en el que las soluciones factibles se pueden descomponer en una secuencia de elementos (caminos en un grafo, series de decisiones, etc.), y cuya funcin objetivo satisface cierta condicin de separabilidad y montona. No obstante, la programacin dinmica tambin encuentra aplicacin en la bsqueda de otras estructuras ptima (rboles ptimos) y en la realizacin eficiente de ciertos clculos de naturaleza recursiva. 11 La programacin dinmica guarda relacin con divide y vencers. Divide y vencers basa su eficiencia en la divisin de un problema en dos o ms subproblemas de igual o parecida talla e inferior a la del original cuyas soluciones se combinan. La divisin de un problema en subproblemas de talla inferior conduce de forma natural a un planteamiento recursivo. Divide y vencers permite disear algoritmos eficientes cuando la divisin proporciona instancias de talla similar (como en mergesort, donde el vector se parte en dos vectores de talla similar) y, adems, no hay llamadas repetidas en el rbol de las llamadas recursivas (recordemos el problema del calculo de la potencia entera de un numero). El mtodo de programacin dinmica se aplica, precisamente, en problemas que adolecen de estos inconvenientes. Para ellos, la programacin dinmica ofrece un repertorio de tcnicas que permiten reducir la complejidad computacional propia de una resolucin puramente recursiva. La fundamental consiste en evitar la repeticin de clculos en las llamadas recursivas mediante el almacenamiento de los resultados que ya han sido calculados para su posterior reutilizacin. Esta tcnica se conoce como (memorizacin). Por otra parte, una transformacin recursivo-iterativa permite, en ocasiones, reducciones adicionales de complejidad espacial: no solo ahorra la memoria que ocupa la pila de llamadas a funcin, sino que permite reducir el propio tamao de la tabla de soluciones precalculadas. La programacin dinmica consiste en una tcnica que permite determinar de manera eficiente las decisiones que optimizan el comportamiento de un sistema que evoluciona a lo largo de una serie de etapas. En otras palabras, trata de encontrar la secuencia de decisiones que optimiza el comportamiento de un proceso polietapico. La naturaleza del razonamiento que se debe realizar en programacin dinmica es muy diferente al de la programacin lineal, intenta describir una determinada situacin en trminos de un modelo matemtico determinado; una vez conocida la naturaleza de las variables de decisin, y expresadas la funcin objetivo y las restricciones en funcin de esas variables, la programacin dinmica no admite una resolucin sistemtica de este tipo; mas que un modelo concreto, es una estrategia de resolucin comn a muchas situaciones en principio diferentes entre si. Adems, es frecuentemente que la resolucin del modelo este muy relacionada con la situacin que se ha de modelizar. En contrapartida las simplificaciones que en ocasiones deben realizarse en programacin lineal para poder resolver el modelo no son necesarias en programacin dinmica, que admite gran variedad de relaciones entre variables. Existen dos tipos generales de programacin dinmica: Programacin dinmica no homognea, frente a programacin dinmica homognea en el tiempo. Para este ultimo caso, podremos plantearnos encontrar la solucin para horizonte finito o para horizonte infinito. Programacin dinmica determinista, frente a programacin dinmica aleatoria, en este caso, es interesante destacar que las cadenas de Harkov con remuneracin y decisin son un caso particular de programacin dinmica aleatoria homognea en el tiempo. 12 Para que un problema pueda ser abordado por esta tcnica ha de cumplir dos condiciones: La solucin al problema ha de ser alcanzada a travs de una secuencia de decisiones, una en cada etapa. Dicha secuencia de decisiones ha de cumplir el principio de ptimo. En grandes lneas, el diseo de un algoritmo de Programacin Dinmica consta de los siguientes pasos: 1. Planteamiento de la solucin como una sucesin de decisiones y verificacin de que sta cumple el principio de ptimo. 2. Definicin recursiva de la solucin. 3. Clculo del valor de la solucin ptima mediante una tabla en donde se almacenan soluciones a problemas parciales para reutilizar los clculos. 4. Construccin de la solucin ptima haciendo uso de la informacin contenida en la tabla anterior. 3.iii. Procesos polietapicos de decisin. Las situaciones susceptibles de ser representadas mediante programacin dinmica pueden describirse como procesos polietapicos de decisin. Seguidamente se exponen algunas caractersticas propias de este tipo de procesos. El problema puede dividirse en etapas. En cada una de esas etapas, debe tomarse una decisin. Tendremos la solucin del problema cuando conozcamos la decisin ptima para cualquier situacin que pueda presentarse en la evolucin del sistema. La programacin dinmica va asociada a situaciones de evolucin de un sistema que va evolucionando a lo largo de varias etapas (de ah su carcter dinmico). En la mayora de las ocasiones, se tratara de representar el comportamiento de un sistema que evoluciona a lo largo del tiempo. En otros casos, se trata de situaciones en las que las decisiones se toman de manera simultnea en el tiempo, pero en las que se evalan las decisiones de manera secuencial. Ntese la diferencia con la programacin lineal, en las que las decisiones se toman de manera simultanea (aunque en ocasiones representemos sistemas que evolucionaran a lo largo del tiempo, como los planes de produccin). Una vez tomada la decisin en el estado correspondiente, el sistema evolucionara hacia alguno de los estados posibles para la etapa siguiente. Por lo tanto, el comportamiento del sistema puede percibirse como una secuencia de decisiones y evoluciones. Dicha evolucin puede ser conocida con certeza, una vez tomada la decisin (tendremos una situacin de programacin dinmica determinista), o bien el sistema puede evolucionar hacia diferentes estados, segn una ley de probabilidad conocida (siendo entonces programacin dinmica aleatoria). El objetivo de la programacin dinmica es de encontrar cual es la poltica optima para cada una de las etapas de la evolucin del sistema. La poltica para 13 una determinada etapa es la decisin ptima en cada uno de los posibles estados del sistema en dicha etapa. Ntese que, para cada etapa, debe definirse una variable de decisin Xn. Si el sistema tiene k estados en esa etapa, una poltica ser un vector de k componentes, cuyas componentes e-sima es el valor de las variables de decisin para el estado e en la etapa n. La esencia de la estrategia de la programacin dinmica se expresa mediante el principio de la optimalidad: En un modelo de programacin dinmica, la poltica ptima para las etapas que faltan hasta la finalizacin del proceso es independiente de las polticas adoptadas en las etapas anteriores. Ejemplo :CLCULO DE LOS NMEROS DE FIBONACCI El siguiente problema trata del clculo de los trminos de la sucesin de nmeros de Fibonacci, mediante la programacin dinmica. Dicha sucesin podemos expresarla recursivamente en trminos matemticos de la siguiente manera: Si n=0.1 Fib(n) Fib(n- 1)+Fib(n- 2) si n>1 Por tanto, la forma ms natural de calcular los trminos de esa sucesin es mediante un programa recursivo: Funcion FibRec(n:CARDINAL):CARDINAL; IF n<=1 THEN RETURN 1 ELSE RETURN FibRec(n-1) + FibRec(n-2) END END FibRec; El inconveniente es que el algoritmo resultante es poco eficiente ya que su tiempo de ejecucin es de orden exponencial, como se vio en el primer captulo. Como podemos observar, la falta de eficiencia del algoritmo se debe a que se producen llamadas recursivas repetidas para calcular valores de la sucesin, que habindose calculado previamente, no se conserva el resultado y por tanto es necesario volver a calcular cada vez. Para este problema es posible disear un algoritmo que en tiempo lineal lo resuelva mediante la construccin de una tabla que permita ir almacenando los clculos realizados hasta el momento para poder reutilizarlos: El algoritmo iterativo que calcula la sucesin de Fibonacci utilizando tal tabla es: TYPE TABLA = ARRAY [0..n] OF CARDINAL PROCEDURE FibIter(VAR T:TABLA;n:CARDINAL):CARDINAL; VAR i:CARDINAL; BEGIN 14 IF n<=1 THEN RETURN 1 ELSE T[0]:=1; T[1]:=1: FOR i:=2 TO n DO T[i]:=T[i-1]+T[i-2] END; RETURN T[n] END END FibIter; Existe an otra mejora a este algoritmo, que aparece al fijarnos que nicamente son necesarios los dos ltimos valores calculados para determinar cada trmino, lo que permite eliminar la tabla entera y quedarnos solamente con dos variables para almacenar los dos ltimos trminos: Funcion FibIter2(n: CARDINAL):CARDINAL; VAR i,suma,x,y:CARDINAL; (* x e y son los 2 ultimos trminos *) IF n<=1 THEN RETURN 1 ELSE x:=1; y:=1; FOR i:=2 TO n DO suma:=x+y; y:=x; x:=suma; END; RETURN suma END END FibIter2; Aunque esta funcin sea de la misma complejidad temporal que la anterior (lineal), consigue una complejidad espacial menor, pues de ser de orden O(n) pasa a ser O(1) ya que hemos eliminado la tabla. El uso de estructuras (vectores o tablas) para eliminar la repeticin de los clculos, pieza clave de los algoritmos de Programacin Dinmica, hace que en este captulo nos fijemos no slo en la complejidad temporal de los algoritmos estudiados, sino tambin en su complejidad espacial. En general, los algoritmos obtenidos mediante la aplicacin de esta tcnica consiguen tener complejidades (espacio y tiempo) bastante razonables, pero debemos evitar que el tratar de obtener una complejidad temporal de orden polinomio conduzca a una complejidad espacial demasiado elevada. 15 3. CONCLUSIONES. Bueno, al terminar de desarrollar este trabajo, espero que el lector, comprenda la importancia de la programacin dinmica, quizs me haya sobrepasado en lo que se refiere a la descripcin de la programacin recursiva, pero como habr ido leyendo, esta forma base de la programacin dinmica, ya que esta no es mas que una mejora de la recursividad, para aumentar su eficiencia. Tambin pienso que quizs no se si habr quedado demasiado claro mis explicaciones, pero es con el material que e contando, ya que aunque es una de las tcnicas mas utilizadas, ya que sirve para optimizar, se aprende mas a utilizarla en su propia aplicacin, e puesto el mismo ejemplo en las dos tcnicas de programacin, la sucesin de Fibonacci, ya que es un ejemplo muy genrico y fcil de ver, amen de otros pequeos ejemplos, que explican partes y aplicaciones de la teora. 16 4. Bibliografa. Libros: Fundamentos de programacin. Algoritmos. Estructuras de datos y objetos. Editorial Mc Graw Hill. Luis Joyanes Aguilar Tcnicas de Diseo de Algoritmos. Servicio de Publicaciones de la Universidad de Mlaga. 1998 Rosa Guerequeta y Antonio Vallecillo Webs: www.tumaster.com/programacion_dinamica-res495.htm www.udlap.mx/~is110493/seminario/algoritmos/ thales.cica.es/rd/Recursos/ rd99/ed99-0033-04/din_introd.html aulavirtual.uji.es/mod/resource/view.php?id=4017 www.lcc.uma.es/~av/Libro/CAP5.pdf www.lcc.uma.es/~lopez/apuntes/ progdec/apuntes/basicas/basicas4pp.pdf www.infor.uva.es/~belar/Archivos%20Ampliacion/ www.dlsi.ua.es/~carrasco/aa/ Algoritmia avanzada Recursividad, ManualesDelWeb.com www.manualesdelweb.com/manual-114-Recursividad.html - 60k www2.ing.puc.cl/~jabaier/iic2552/progdin.pdf -
Realidad Aumentada Como Estrategia Didáctica en Curso de Ciencias Naturales de Estudiantes de Quinto Grado de Primaria de La Institución Educativa Campo Valdés PDF