Está en la página 1de 18

Programación Dinámica

Curso 2003/2004
Daniel García
José Moya
José A. Gallud

Programación Dinámica 1
1. Introducción
– En DV los ejemplares se dividen en subejemplares
• Los subejemplares se resolvían (mediante subdivisiones)
• Se combinan las soluciones para resolver el caso general
– DV no tiene en cuenta los posibles solapamientos
• Si los subproblemas idénticos se resuelven independientemente, se
duplica el trabajo  algoritmo ineficiente
• Podemos almacenar las subsoluciones para su uso posterior, evitando
la duplicación y resultando más eficaces
– PD evita calcular dos veces un mismo resultado, por lo que
necesita una tabla de resultados conocidos
– DV: técnica descendente de refinamiento sucesivo  el caso
completo se va dividiendo en subcasos más pequeños
– PD: técnica ascendente que comienza por los subcasos más
pequeños y sencillos

Programación Dinámica 2
2. Cálculo del coeficiente binomial
1 si k=0 o k=n
n n-1 n-1
+ si 0<k<n
k k-1 k

0 en caso contrario

– Para calcularlo directamente:


funcion C(n,k)
si k=0 o k=n entonces devolver 1
sino devolver(C(n-1,k-1)+C(n-1,k))
fin_funcion
– Para calcular C(5,3):
C(5,3) = C(4,2)+C(4,3) = C(3,1)+C(3,2)+C(3,2)+C(3,3) =
= C(2,0)+C(2,1)+C(2,1)+C(2,2)+C(2,1)+C(2,2) =
= 1+C(1,0)+C(1,1)+1+C(1,0)+C(1,1)+1+1 = 10

Programación Dinámica 3
– Muchos valores C(i,j) i<n , j<n se calculan muchas veces 
C(2,2) o C(2,1)
• La repetición de cálculos tiene orden exponencial
– El resultado final se obtiene sumando un cierto número de 1, luego
el tiempo de ejecución de la función es ((nk))
– Se puede mejorar el algoritmo utilizando una tabla de resultados
intermedios (triángulo de Pascal), alcanzándose (n·k)
0 1 2 ..... k-1 k
0 1 0 0 0 0
1 1 1 0 0 0
2 1 2 1 0 0 0
3 1 3 3 1
......
n-1 1
n 1
Programación Dinámica 4
3. El problema de devolver el cambio
– Análisis del algoritmo voraz:
• Es muy eficiente cuando funciona
• Funciona en un número limitado de casos:
– En ciertos sistemas monetarios o cuando faltan monedas de algún tipo 
obtiene una respuesta no óptima o incluso no obtiene respuesta
– Ejemplo: devolver 8 unidades con monedas de 1, 4 y 6
» Voraz: 1moneda de 6 y 2 de 1  total 3 monedas
» Mejor: 2 monedas de 4  total 2 monedas
– PD: preparar una tabla con los resultados intermedios útiles, para
combinarlos en la solución del caso
– Suponemos que:
• El sistema monetario tiene n tipos de monedas
• Cada moneda i, 1 i  n tiene un valor di unidades di>0
• Se dispone de un suministro ilimitado de monedas
• Cambio a devolver: N unidades con el menor número de monedas

Programación Dinámica 5
– Elementos para resolver el problema mediante PD:
• Tabla c[1..n,0..N] con una fila para las posibles denominaciones o
tipos de moneda y cada columna para las cantidades posibles a pagar
(desde 0 hasta N)
• C[i,j]: número mínimo de monedas necesarias para pagar la cantidad
de j unidades con 0  j  N empleando los tipos de moneda de la 1 a
la i con 1  i  n
• Para rellenar la tabla:
– Inicialmente c[i,0]=0 para todo i
– Para pagar j unidades con monedas de los tipos 1 a i:
» Sin utilizar monedas de tipo i  c[i,j] = c[i-1,j]
» Utilizando al menos una moneda de tipo i:
c[i,j] = 1 + c[i, j-di]
– Elegiremos la que minimice el número de monedas utilizadas:
c[i,j] = min(c[i-1,j], 1 + c[i, j-di] )
– Si i=1 y j<d1 haremos c[i,j] = + para indicar que no es posible pagar una
cantidad j exacta empleando sólo monedas del tipo 1
– Cuando i=1 o j<di, uno de los elementos que se compara no está en la
tabla

Programación Dinámica 6
– Cuando i=1 o j<di, uno de los elementos que se compara no está
en la tabla

Ejemplo: pagar 8 unidades con monedas de 1, 4 y 6

Funcion monedas(N)
Vector d[1..n]=[1,4,6]
Matriz c[1..n,0..N]
Para i=1 hasta n hacer c[i,0] = 0
Para i=1 hasta n hacer
para j=1 hasta N hacer
si i=1 y j<d[i] entonces c[i,j] = 
sino si i=1 entonces c[i,j] = 1 + c[1,j-d[1]]
sino si j < d[i] entonces c[i,j] = c[i-1,j]
sino c[i,j] = min(c[i-1,j], 1 + c[i,j-d[i]])
devolver c[n,N]

Programación Dinámica 7
– Con un suministro inagotable de monedas de tipo 1, siempre
existe solución
» en otro caso puede haber valores de N sin solución
• Para saber las monedas (tipo y cantidad) de la solución:
– Pagar j con monedas 1,2,...,i
– c[i,j] = nº mínimo de monedas que se necesitan para pagar j
– Si c[i,j] = c[i-1,j]  no se necesitan monedas de tipo i y se tiene
que comprobar c[i-1,j]
– Si c[i,j] = 1 + c[i,j-di]  se entrega una moneda i y se avanza
hasta c[i,j-di] que también se tiene que comprobar
– Si c[i-1,j] y 1+c[i,j-di] son iguales a c[i,j] se puede seleccionar
cualquiera
– Se sigue el mismo proceso hasta que j=0
• Análisis del algoritmo:
– Calcular una matriz nx(N+1)  tiempo de ejecución (n·N)
– Menos eficiente que voraz aunque es óptimo

Programación Dinámica 8
– Principio de optimalidad
• En una sucesión óptima de decisiones u opciones, toda subsecuencia
debe ser también óptima
– En el algoritmo de devolver el cambio
» c[i,j] forma óptima de devolver j con monedas 1..i 
» c[i-1,j] y c[i,j-di] son también soluciones óptimas de los casos que
representan
» Sólo nos interesaba c[n,N], pero el resto de entradas también
proporcionan soluciones óptimas
• Si el principio no es aplicable en un escenario, es probable que no sea
posible utilizar programación dinámica
– Cuando se trata de utilización óptima de recursos no se aplica del mismo
modo
» Camino más corto entre A-C que pasa por B: A-B y B-C son
también los más cortos
» Camino más rápido entre A-C que pasa por B: no implica
necesariamente que A-B o B-C tengan que ser también los más
rápidos
– Los subcasos tienen que ser independientes

Programación Dinámica 9
• Principio de optimalidad: se cumple si la solución óptima de
cualquier caso no trivial de un problema es una combinación
de soluciones óptimas de algunos subcasos
– No es sencillo trasladarlo a un algoritmo: no es evidente saber
cuáles son los subcasos relevantes para el caso considerado
– Esta dificultad impide aplicar una aproximación similar a DV,
comenzando por el caso original y buscando recursivamente
soluciones óptimas
• La programación dinámica resuelve todos los subcasos,
determinando los que son relevantes, para combinarlos en una
solución óptima del caso original

Programación Dinámica 10
– El problema de la mochila
• N objetos, i=1,...,n  wi>0 y vi>0 (peso y valor)
• W: peso máximo de la mochila
• Objetivo: llenar la mochila maximizando el valor y respetando la
limitación de capacidad
– xi=0 no cogemos el objeto i; xi=1 si cogemos el objeto i
– Maximizar: xivi cumpliendo que  xiwiW
– No se pueden fragmentar los objetos
» El algoritmo voraz no funcionaba sin fragmentar (...)
• PD:
– Tabla V[1..n,0..W]: una fila por objeto; una columna por peso
– V[i,j]: valor máximo de los objetos que podemos transportar con un límite
de peso j, 0  j  W, incluyendo los objetos numerados desde 1 a i, 1  i 
n
– Solución: V[n,W]
– V[i,j]=max(V[i-1,j],V[i-1,j-w i]+vi)
» V[i,0]=0 si j0; V[0,j]=0 cuando j 0 y V[i,j]=- , i cuando j<0 (...)
– Análisis del algoritmo con p.d.:
» Construir la tabla (n·W)
» Composición de la carga óptima O(n+w)

Programación Dinámica 11
– Caminos mínimos: longitud más corta entre nodos
• G=<N,A> grafo dirigido;
– N: conjunto de nodos = {1,2,...,n}
– A: conjunto de aristas, cada arista tiene una longitud >0
• Objetivo: calcular el camino más corto entre cada par de nodos
L=matriz con las longitudes de las aristas
L[i,j]=0, i=1,2,...,n
L[i,j]0 si (i,j)
L[i,j]= si no (i,j)
– Es aplicable el principio de optimalidad: si k es un nodo del camino
mínimo entre i y j, entonces:
» la parte del camino que va desde i hasta k y
» la parte del camino de k a j, también son óptimos
– Construimos matriz D: D[i,j]=longitud camino más corto de i a j
– Inicialmente: D=L (distancias directas entre nodos)
– Después de k iteraciones:
» Dk: longitud de los caminos más cortos que utilizan los nodos 1..k
– Después de n iteraciones (todos los nodos):
» Dn: longitud de los caminos más cortos que utilicen alguno de los
nodos de N como nodo intermedio

Programación Dinámica 12
– En la iteración k: (1kn):
» Se comprueba para cada par de nodos (i,j) si  o no un camino que
vaya de i a j pasando por el nodo k, y que sea mejor que el camino
óptimo actual que pasa por los nodos {1,2,...,k-1}
» Dk[i,j]=min(Dk-1[i,j],Dk-1[i,k]+Dk-1[k,j])
– Algoritmo de Floyd:
Funcion Floyd(L[1..n,1..n]):matriz[1..n,1..n]
matriz D[1..n,1..n]
D=L
Para k=1 hasta n hacer
para i=1 hasta n hacer
para j=1 hasta n hacer
D[i,j]=min(D[i,j],D[i,k]+D[k,j])
Devolver D
– Complejidad: (n3)
– Dijkstra (voraz): resuelve el problema aplicándolo n veces, eligiendo
cada vez un nodo distinto como origen (n·n2)=(n3)
» Floyd es más sencillo; la cte oculta es más pequeña  más rápido

Programación Dinámica 13
– Para saber qué nodos forman el camino más corto
» Introducimos una matriz P con valor inicial 0
» P[i,j]=nº de la última iteración (k) que haya modificado D[i,j]
» El algoritmo de Floyd quedaría así (en el último bucle):
si D[i,k]+D[k,j]<D[i,j] entonces
D[i,j]=D[i,k]+D[k,j]
P[i,j]=k
» Para recuperar el camino más corto de i a jP[i,j]:
» Si P[i,j]=0 D[i,j] no ha cambiado y el camino mínimo pasa por la
arista (i,j)
» Si P[i,j]0 =k el camino más corto de i a j pasa por k
» Se examina recursivamente P[i,k] y P[k,j] buscando otros nodos
intermedios
– Ejemplo: (...)
– Longitudes negativas:
» ¿qué ocurre si se permiten longitudes negativas?
» Calcular los caminos simples más cortos en grafos con longitudes
negativas (simple: nunca se visita dos veces un nodo)
» No se conoce un algoritmo eficiente: problema NP-completo

Programación Dinámica 14
– Grafos multietapa
• Grafo dirigido G(N,A), cuyos nodos están reagrupados en k
subconjuntos disjuntos: V1, V2, ...,Vk llamados etapas
• Propiedades:
– GM1: N=i=1 Vi  i=1 Vi=
– GM2: si los nodos x,yVi (x,y), (y,x)A
» No existe ningún arco que tenga por extremos nodos de un mismo
conjunto Vi
– GM3: si (x,y)A y xVi, yVj  [i<j] [j=i+1]
» Los arcos que entran en un conjunto lo hacen desde una etapa
anterior; los que salen, lo hacen hacia adelante
– GM4: a(x)=conjunto de antecesores del nodo x (y,x), ya(x)
 s(x)=conjunto de sucesores de x, (x,y), con ys(x)
 a(x)=  si i=1, xVi
 s(x)=  si i=n, xVi
» Cualquier nodo tiene antecesores excepto los de la etapa 1
» Cualquier nodo tiene sucesores excepto los de la última etapa
– GM5: |V1|=|Vk|=1

Programación Dinámica 15
– El camino óptimo con grafos multietapa
• Grafo multietapa de k etapas
– Cada arco tiene un valor o coste asociado
– Problema: encontrar el camino óptimo para ir desde V1 hasta Vk.
• Resolución con PD:
– Notación:
» el nodo s sobre V1 tendrá índice 1
» El nodo t sobre Vk tendrá índice n
» Se asigna el número de nodo por etapas, correlativamente
– Subproblemas:
» Para cada nodo de cada Vi, se estudia el mejor camino desde V1
hasta el nodo Vi; igualmente de i a k
– Principio de optimalidad:
» Si el nodo xal camino óptimo entre V1 y Vk, el subcamino V1..x es
óptimo y el subcamino x..t, con tVk, también es óptimo
– Planteamiento:
» L[1..n,1..n]=coste de las aristas del grafo; k: nº de etapas
» P[1..k]=vector que contiene el camino óptimo; P[i]=nodo antecesor
» Coste[1..n]=coste del camino óptimo (de s a i)
» Costei[j]=[max/min](costei-1(l)+L(l,j))  lVi-1; (l,j) A
Programación Dinámica 16
• Algoritmo del camino óptimo con grafo multietapa(COGM)
Funcion COGM(L[1..n,1..n],k):vector p[k]
vector Coste[n], Aux[n]
Coste[1]=0
Para j=2 hasta n hacer
r = nodo tal que (r,j)A y Coste[r]+L(r,j) sea óptimo
Coste[j] = Coste[r]+L[r,j]
Aux[j]=r
Fin para
Si |V1|=1 entonces P[1]=1
Si |Vk|=1 entonces P[k]=n
Para j=k-1 hasta 2 hacer
P[j] = Aux[P[j+1]]
Fin para
Fin COGM
– El coste del camino óptimo está en Coste[n]
– Los nodos del camino óptimo están en P[1..k]
– Ejemplo: (...)
Programación Dinámica 17
– Aplicaciones de los grafos multietapa
• Muchos problemas de PD se pueden resolver con GM
• Problema de la mochila con GM
– Construir grafo multietapa; nº etapas=nº objetos
– Etiquetar los nodos con las posibles capacidades de la mochila
– De cada nodo salen dos aristas:
a) Irá hasta otro nodo con la misma etiqueta (capacidad) de una
etapa posterior
b) Irá hasta otro nodo etiquetado con la capacidad del vértice de
salida mas Pi (el peso de ese nodo)
– Solamente habrá un arco si la etiqueta de un nodo es menor que P i (W-
etiqueta<Pi)
• Ejemplo: W=6, n=5 objetos  (...)
– Aplicación del principio de optimalidad al problema de la
mochila:
• Si 1,2,...,t es la selección óptima de objetos para el problema de
llenar la mochila, con W de peso máximo, con objetos de un
conjunto C 
– La subsolución 1, 2,..., t-1 es solución óptima del problema de la
mochila con capacidad W-Pt y el conjunto C-{t}
Programación Dinámica 18

También podría gustarte