Universidad Da Vinci Dr. Vicente Cubells (vcubells@udavinci.edu.mx)
Temario Técnicas de diseño de algoritmos Divide y Vencerás Programación dinámica
Introducción Al desarrollar un algoritmo computacional que nos
permita resolver un problema, podemos tomar ventaja de diferentes técnicas Estas técnicas nos dan una guía para poder desarrollar de manera más rápida, una buena solución
Divide y Vencerás… Divide and Conquer Es una técnica para el diseño de algoritmos. El principio en el que se basa es el siguiente: Es más fácil resolver varios casos pequeños de un problema que un
solo caso grande.
El enfoque de divide y vencerás está formado por etapas: Dividir el problema en ejemplares más pequeños. Resolver los problemas pequeños. Combinar las soluciones parciales para obtener la solución al
problema original.
Se resuelve directamente para un caso simple o se divide el problema en 2 o más subproblemas.
Divide y Vencerás… Ejemplo de diseño de algoritmo del problema I:
resolver(I) { n = tamaño(I); if( n <= pequeño ) solucion = resolverDirecto(I); else { dividir I en I1, I2, ...Im. Para toda i: si = resolver(Ii); solucion = combinar(s1, s2,..., sm); } return solucion; }
Divide y Vencerás… Ejemplo donde se quieren dibujar las divisiones de
una regla de la siguiente manera:
Se comienza por ejemplo con una regla que va de 0 a
30 cms, que se divide a la mitad y se hace una marca de altura h.
Divide y Vencerás… Luego cada mitad, es decir de 0 a 15cms. y de 15 a 30
cms, se vuelve a dividir a la mitad y se hacen marcas con una altura h1.
Otra vez cada sección se divide a la mitad y se marca.
Se deja de marcar cuando la altura de la raya sea
menor o igual a cero.
Divide y Vencerás… Algoritmo: pintarRegla( int inicio, int final, int altura ) { if( altura <= 0 ) return; else { posicion = (inicio + final)/2; marcar( posicion, altura ); h1 = altura / 2; pintarRegla( inicio, posicion, h1); pintarRegla( posicion, final, h1);
}
}
Divide y Vencerás… Algoritmo recursivo de búsqueda
binaria
int buscar( int a[], int valor, int ini, int fin ) { int medio; if ( fin < ini ) return -‐1; medio = ( ini + fin ) / 2; if ( a[medio] > valor ) return buscar( a, valor, ini, medio-‐1); else if ( a[medio] < valor ) return buscar( a, valor, medio+1, fin ); else return medio; }
Divide y Vencerás Algoritmo iterativo de búsqueda
binaria
int buscar( int a[], int valor, int ini, int fin ) { int medio; while ( ini <= fin ) { medio = ( ini + fin ) / 2; if ( a[medio] > valor ) fin = medio -‐ 1; else if ( a[medio] < valor ) ini = medio + 1; else return medio; } return -‐1; }
Programación dinámica… El inconveniente se presenta cuando los
subproblemas obtenidos no son independientes sino que existe solapamiento entre ellos es cuando una solución recursiva no resulta eficiente
por la repetición de cálculos que conlleva
En estos casos es cuando la Programación Dinámica
nos puede ofrecer una solución aceptable.
La eficiencia de esta técnica consiste en resolver los
subproblemas una sola vez, guardando sus soluciones en una tabla para su futura utilización.
Programación dinámica… Donde tiene mayor aplicación la Programación Dinámica
es en la resolución de problemas de optimización
En este tipo de problemas se pueden presentar distintas
soluciones, cada una con un valor, y lo que se desea es encontrar la solución de valor óptimo (máximo o mínimo)
Se basa en el llamado principio de óptimo enunciado por
Bellman en 1957 y que dice:
“En una secuencia óptima de decisiones, toda subsecuencia ha de ser también óptima”
Programación dinámica… El diseño de un algoritmo de Programación Dinámica
consta de los siguientes pasos:
Planteamiento de la solución como una sucesión de
decisiones y verificación de que ésta cumple el principio de óptimo Definición recursiva de la solución 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 Construcción de la solución óptima haciendo uso de la información contenida en la tabla anterior
Programación dinámica… Serie de Fibonacci:
Ver algoritmo recursivo… Analizar utilización de una tabla
Fib(0)
Fib(1)
Fib(2)
…
Fib(n)
Programación dinámica… int FibonacciIterativo(int n) { int tabla[n]; if (n <= 1) return 1; else { T[0] = 1; T[1] = 1; for (int i=2 ; i < n ; i++) { T[i] = T[i-1] + T[i-2]; } return T[n]; } }
Programación dinámica… int FibonacciIterativo(int n) { int suma, x, y; if (n <= 1) return 1; else { x = 1; y = 1; for (int i=2 ; i < n ; i++) { suma = x + y; y = x; x = suma; } return suma; } }
Programación dinámica… Cálculo de coeficientes binomiales
Programación dinámica… Cálculo de coeficientes binomiales
Programación dinámica… int CoefIter(int n, int k) { int i,j; int C[n][k]; for (i=0 ; i < n ; i++) { C[i,0] = 1; } for (i=1 ; i < n ; i++) { C[i,1] = i; } for (i=2 ; i < k ; i++) { C[i,i] = 1; } for (i=3 ; i < n ; i++) { for (j=2 ; j < i-1 ; j++) if (j<=k) { C[i,j] = C[i-1,j-1]+C[i-1,j]; } } }
return C[n,k]; }
Programación dinámica… Función de Ackerman
En cada paso m del algoritmo hay que actualizar los Ack[i] (1 ≤ i ≤ n)
Ack[] suficientemente grande pues m crece muy rápido
Resumiendo La técnica Divide y Vencerás puede aplicarse para
resolver problemas descomponiendo en subproblemas más pequeños Funciona cuando existe un independencia total entre
las subsoluciones
Si no existe independencia entre las subsoluciones,
lo mejor es utilizar Programación Dinámica, siempre y cuando se cumpla el principio del óptimo