Grado en Ingeniería Informática
Metodología de la programación
Metodología de la programación. Trabajo de programación dinámica.
Grupo A.1 Curso 2011/12
Boris Fajardo Peral Martín Cambronero Honrubia
Boris Fajardo Peral & Martín Cambronero Honrubia
1
Grado en Ingeniería Informática
Metodología de la programación
Programación dinámica. Existen una serie de problemas cuyas soluciones pueden ser expresadas recursivamente en términos matemáticos, y posiblemente la manera más natural de resolverlos es mediante un algoritmo recursivo. Sin embargo, el tiempo de ejecución de la solución recursiva puede mejorarse substancialmente mediante la Programación Dinámica. Dicha programación no sólo tiene sentido aplicarla por razones de eficiencia, sino porque presenta un método capaz de resolver problemas cuya solución ha sido abordado por otras técnicas y ha fracasado. Donde tiene mayor aplicación es en la resolución de problemas de optimización ya que en este tipo de problemas se suelen encontrar distintas soluciones y lo que se desea es encontrar la solución de valor óptimo. La solución de problemas mediante esta técnica 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 también óptima”.
Pongamos un ejemplo para explicar el enunciado de Bellman: Queremos que un viajante realice un viaje de Madrid a Barcelona, si encontramos el camino óptimo del viaje tenemos que el camino desde cualquier punto del recorrido hasta el punto inicial (Madrid) y al hasta el punto final (Barcelona) es Óptimo. Para que un problema pueda ser resuelto con esta técnica, debe cumplir con ciertas características: El problema se puede dividir en etapas. Cada etapa tiene un número de estados asociados a ella. La decisión óptima de cada etapa depende solo del estado actual y no de las decisiones anteriores. La decisión tomada en una etapa determina cual será el estado de la etapa siguiente.
Curso 2011/12
Boris Fajardo Peral & Martín Cambronero Honrubia
2
Grado en Ingeniería Informática
Metodología de la programación
El diseño de un algoritmo de programación dinámica consta de aproximadamente los siguientes pasos: 1. Planteamiento de la solución como una sucesión de decisiones y verificación de que ésta cumple el principio de óptimo. 2. Definición recursiva de la solución. 3. Cálculo del valor de la solución óptima mediante una tabla en donde se almacenan soluciones a problemas parciales para reutilizar los cálculos. 4. Construcción de la solución óptima haciendo uso de la información contenida en la tabla anterior.
Problema. Se denomina filotaxis a la disposición que presentan las hojas en el tallo. La disposición que presentan es característica de cada especie y tiene la función de que las hojas estén expuestas al sol con el mínimo de interferencias posibles por parte de sus compañeras.
A lo largo de los años, los científicos han comprobado que existe una relación directa entre la filotaxis y los números de la sucesión de Fibonacci. Contando las espiras según la trayectoria de hojas, escamas (piñas) o estambres (girasoles), se tiene que el número de espiras en ambos sentidos nunca es el mismo y siempre coinciden con números consecutivos de la sucesión de Fibonacci. Se pretende diseñar un algoritmo para determinar, en función de este principio, si un vegetal pertenece o no al mundo real, dando el número de espiras como parámetro. El algoritmo consiste en generar números según la sucesión de Fibonacci, haciendo las comprobaciones oportunas para determinar si pertenece o no. Siendo Fib[i] un array de enteros, n y m el número de espirales en cada sentido ordenado tal que n
Curso 2011/12
1 Fib(i − 1) + Fib(i − 2)
𝑠𝑖 𝑖 = 0 ∨ 𝑖 = 1 en otro caso
Boris Fajardo Peral & Martín Cambronero Honrubia
3
Grado en Ingeniería Informática
Metodología de la programación
Si Fib[i] > n && Fib[i-1]= m) && (Fib[i]>m || Fib[i-1] != n) m no pertenece a la sucesión || m y n no son consecutivos, devuelvo 0. Else devuelvo 1.
Como puede verse, el algoritmo se basa en una tabla de resultados (unidimensional). Se tiene una sucesión de etapas. Cada etapa tiene un estado asociado. A partir de este estado se toma una decisión que afectará o no al siguiente estado. Se evita la recursividad, que produciría cálculos innecesarios.
Ejemplo 1: Tenemos la sospecha de que Nickelodeon intenta engañar a nuestros hermanos haciéndoles creer que Bob Esponja vive en una piña debajo del mar. Haciendo uso de nuestro algoritmo, vamos a demostrar matemáticamente que, aun que una piña pudiera vivir en el fondo del mar, eso no es una piña.
Curso 2011/12
Boris Fajardo Peral & Martín Cambronero Honrubia
4
Grado en Ingeniería Informática
Metodología de la programación
Como podemos ver claramente, se forman 5 espiras (líneas) en cada sentido. Si introducimos estos valores en nuestro algoritmo:
El programa nos dice que esa piña no puede ser real, ya que el número de espiras, aun que pertenece a la sucesión de Fibonacci, no es consecutivo.
Ejemplo 2: Vamos a demostrar que nuestro algoritmo puede demostrar que el girasol de la siguiente foto pertenece al mundo real.
Si nos dedicamos a contar las espiras del girasol llegamos a la conclusión de que tenemos 21 espiras en un sentido y 34 en el otro.
Curso 2011/12
Boris Fajardo Peral & Martín Cambronero Honrubia
5
Grado en Ingeniería Informática
Metodología de la programación
Introduciendo dichos valores a nuestro algoritmo:
El programa determina que efectivamente, cumple la condición y por tanto no tenemos evidencia de que no pertenezca al mundo real.
Análisis del coste computacional. int fib(int n, int m){ int secuencia[MAX]; int i=1, aux; secuencia[0]= 1; if(n>m){ /* ordeno los valores, primero el menor */ aux=n; n=m, m=aux; } printf("\nSecuencia de Fibonacci: "); /* Se genera la secuencia de Fibonacci*/ while(m > secuencia[i-1]){ if(i<=1) secuencia[i]=1; else secuencia[i] = secuencia[i-1] + secuencia[i-2]; printf("%d
", secuencia[i]);
/* Si n no está en la secuencia, termino */ if(secuencia[i] > n && secuencia[i-1] < n) return 0; i++; } if(secuencia[i-1] > m) return 0; /* m != Fib[i] */ if(secuencia[i-2] != n) return 0; /* n no consecutivo */ return 1; }
Curso 2011/12
Boris Fajardo Peral & Martín Cambronero Honrubia
6
Grado en Ingeniería Informática
Metodología de la programación
El mayor coste viene dado por: while(m>Fib[i]){ Fib[i] = Fib[i-1]+Fib[i-2]; i++; } La forma en la que “crece” Fib[i], partiendo de t(0)=1 y t(1)=1, viene dada por la siguiente expresión: t(n) = t(n-1)+t(n-2)
Que reescribiéndola es: t(n)-t(n-1)-t(n-2)=0 El polinomio característico:
Cuyas raíces son:
Por lo que la solución general es de la forma:
Según las condiciones iniciales t(0)=1 y t(1)=1:
Curso 2011/12
Boris Fajardo Peral & Martín Cambronero Honrubia
7
Grado en Ingeniería Informática
Metodología de la programación
Si resolvemos el sistema:
Sustituyendo en la ecuación general, tenemos: 𝑡(𝑛) = 1 · 1.6𝑛 ∈ 𝑂(1𝑛 ) Luego, el coste de nuestro algoritmo es de 𝑶(𝒍𝒐𝒈(𝒎))
Curso 2011/12
Boris Fajardo Peral & Martín Cambronero Honrubia
8