Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Sin embargo esto pude agrandar mucho el tamaño del problema. Surge así
Programación Dinámica (PD) como una alternativa de descomposición en que resolvemos
subproblemas más pequeños y luego los ligamos.
PROGRAMACIÓN DINÁMICA
1. HISTORIA
El término Programación Dinámica fue utilizado originalmente en los 1940’s por
Richard Bellman para describir el proceso de resolver problemas donde se necesita encontrar las
mejores decisiones una tras otra.
Para 1953, el refinó esto a su significado moderno, el cual se refiere específicamente a anidar
pequeños problemas de decisión dentro de grandes decisiones, luego de esto el campo fue
reconocido por la lEEE como un tópico de análisis de sistemas e ingeniería.
Sin embargo, actualmente muchos problemas de optimización son mejor resueltos escribiendo
programas de computadoras que implementa un algoritmo de programación dinámica, lo cual
resulta mejor que llevar a cabo cientos de cálculos a mano.
2. DEFINICIÓN
La programación dinámica consiste en una técnica 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 polietápico.
La naturaleza del razonamiento que se debe realizar en programación dinámica es muy diferente
al de la programación lineal. En programación lineal, intenta describir una determinada
situación en términos de un modelo matemático determinado; una vez conocida la naturaleza
de las variables de decisión, y expresadas la función objetivo y las restricciones en función de
esas variables, la resolución del modelo puede confiarse, sin mayores problemas, a un
programa informático. La programación dinámica no admite una resolución sistemática de
este tipo; más que un modelo concreto, es una estrategia de resolución común a muchas
situaciones en principio diferentes entre sí.
Además, es frecuente que la resolución del modelo esté muy relacionada con la situación que
se ha de modelizar. En contrapartida, las simplificaciones que en ocasiones deben realizarse en
programación lineal para poder resolver el modelo no son necesarias en programación dinámica,
que admite gran variedad de relaciones entre variables.
Estos tipos de programación dinámica se introducirán mediante ejemplos, que esperamos que
ayuden al lector a internalizar la lógica de la programación dinámica.
En síntesis, la política óptima desde un estado s de la etapa k a la etapa final está constituida por
una decisión que transforma s en un estado s0 de la etapa k +1 y por la política optima desde
el estado s0 hasta la etapa final.
Cada etapa debe tener asociado una o más decisiones (problema de optimización),
cuya dependencia de las decisiones anteriores está dada exclusivamente por las
variables de estado.
Cada estado debe contener toda la información relevante para la toma de decisión
asociada al periodo.
Las variables de decisión son aquellas sobre las cuales debemos definir su valor de modo
de optimizar el beneficio acumulado y modificar el estado de la próxima etapa.
En otros casos, aunque no sea posible aplicar la estrategia voraz, se cumple el principio de
optimalidad de Bellman que dicta que «dada una secuencia óptima de decisiones, toda
subsecuencia de ella es, a su vez, óptima».
En este caso sigue siendo posible el ir tomando decisiones elementales, en la confianza de que la
combinación de ellas seguirá siendo óptima, pero será entonces necesario explorar muchas
secuencias de decisiones para dar con la correcta, siendo aquí donde interviene la
programación dinámica.
Por tanto, la forma más natural de calcular los términos de esa sucesión es mediante
un programa recursivo:
PROCEDURE FibRec(n:CARDINAL):CARDINAL;
BEGIN
IF n<=1 THEN RETURN 1
ELSE
RETURN FibRec(n-1) + FibRec(n-2)
END
END FibRec;
El algoritmo iterativo que calcula la sucesión de Fibonacci utilizando tal tabla es:
Existe aún otra mejora a este algoritmo, que aparece al fijarnos que únicamente son
necesarios los dos últimos valores calculados para determinar cada término, lo que permite
eliminar la tabla entera y quedarnos solamente con dos variables para almacenar los dos
últimos términos:
Aunque esta función sea de la misma complejidad temporal que la anterior (lineal),
consigue una complejidad espacial menor, pues de ser de orden O(x) pasa a ser O(1) ya
que hemos eliminado la tabla.
El algoritmo recursivo que los calcula resulta ser de complejidad exponencial por la
repetición de los cálculos que realiza. No obstante, es posible diseñar un algoritmo con
un tiempo de ejecución de orden O(nk) basado en la idea del Triángulo de Pascal.
Para ello es necesario la creación de una tabla bidimensional en la que ir almacenando los
valores intermedios que se utilizan posteriormente:
0 1 2 3 ... k-1 k
0 1
1 1 1
2 1 2 1
3 1 3 3 1
... ... ... ... ... ...
... ... ... ... ... ... ...
n-1 C(n-1,k-1) + C(n-1,k)
N C(n,k)
Iremos construyendo esta tabla por filas de arriba hacia abajo y de izquierda a
derecha mediante el siguiente algoritmo de complejidad polinómica:
PROCEDURE CoefIter(n,k:CARDINAL):CARDINAL;
VAR i, j: CARDINAL;
C: TABLA;
BEGIN
FOR i:=0 TO n DO C[i,0]:=1 END;
FOR i:=1 TO n DO C[i,1]:=i END;
FOR i:=2 TO k DO C[i,i]:=1 END;
FOR i:=3 TO n DO
FOR j:=2 TO i-1 DO
IF j<=k THEN
C [i, j]:=C[i-1,j-1]+C[i-
1,j] END
END
END;
RETURN C[n,k]
END CoefIter.
LA SUBSECUENCIA COMÚN MÁXIMA
Hay muchos problemas para los cuales no sólo deseamos encontrar el valor de la solución
óptima sino que además deseamos conocer cuál es la composición de esta solución, es
decir, los elementos que forman parte de ella. En estos casos es necesario ir
conservando no sólo los valores de las soluciones parciales, sino también cómo se
llega a ellas. Esta información adicional puede ser almacenada en la misma tabla que las
soluciones parciales, o bien en otra tabla al efecto.
Veamos un ejemplo en el que se crea una tabla y a partir de ella se reconstruye la solución.
Se trata del cálculo de la subsecuencia común máxima. Vamos en primer lugar a definir el
problema.
Dada una secuencia X={x1 x2 ... xm}, decimos que Z={z1 z2 ... zk} es una
subsecuencia de X (siendo k m) si existe una secuencia creciente {i1 i2 ... ik} de
índices de X tales que para todo j = 1, 2, ..., k tenemos xij = zj.
Dadas dos secuencias X e Y, decimos que Z es una subsecuencia común de X e Y si es
subsecuencia de X y subsecuencia de Y. Deseamos determinar la subsecuencia de
longitud máxima común a dos secuencias.
Solución
INTERESES BANCARIOS
Dadas n funciones f1, f2, ..., fn y un entero positivo M, deseamos maximizar la función
f1(x1) + f2(x2) + ... + fn(xn) sujeta a la restricción x1 +x2 + ... + xn = M, donde fi(0) = 0
(i=1,..,n), xi son números naturales, y todas las funciones son monótonas crecientes, es
decir, x y implica que fi(x) > fi(y). Supóngase que los valores de cada función se
almacenan en un vector.
Este problema tiene una aplicación real muy interesante, en donde fi representa la función
de interés que proporciona el banco i, y lo que deseamos es maximizar el interés total al
invertir una cantidad determinada de dinero M. Los valores xi van a representar la cantidad
a invertir en cada uno de los n bancos.
Solución (.)
Sea fi un vector que almacena el interés del banco i (1 i n) para una inversión
de 1, 2, 3, ..., M pesetas. Esto es, fi(j) indicará el interés que ofrece el banco i para j
pesetas, con 0 < i n , 0 < j M.
Para poder plantear el problema como una sucesión de decisiones, llamaremos
In(M) al interés máximo al invertir M pesetas en n bancos,
In(M) = f1(x1) + f2(x2) + ... + fn(xn)
que es la función a maximizar, sujeta a la restricción x1 +x2 + ... + xn = M.
Veamos cómo aplicar el principio de óptimo. Si In(M) es el resultado de una
secuencia de decisiones y resulta ser óptima para el problema de invertir una
cantidad M en n bancos, cualquiera de sus subsecuencias de decisiones ha de ser
también óptima y así la cantidad
In–1(M – xn) = f1(x1) + f2(x2) + ... + fn–
1(xn–1)
será también óptima para el subproblema de invertir (M – xn) pesetas en n – 1
bancos. Y por tanto el principio de óptimo nos lleva a plantear la siguiente relación
en recurrencia:
Para resolverla y calcular In(M), vamos a utilizar una matriz I de dimensión nxM en donde
iremos almacenando los resultados parciales y así eliminar la repetición de los cálculos.
El valor de I[i,j] va a representar el interés de j pesetas cuando se dispone de i bancos, por
tanto la solución buscada se encontrará en I[n,M]. Para guardar los datos iniciales del
problema vamos a utilizar otra matriz F, de la misma dimensión, y donde F[i,j] representa
el interés del banco i para j pesetas.
En consecuencia, para calcular el valor pedido de I[n,M] rellenaremos la tabla por filas,
empezando por los valores iniciales de la ecuación en recurrencia, y según el siguiente
algoritmo:
Solución
El problema se resuelve invocando a la función con i=n, p=M. La complejidad del algoritmo
viene determinada por la construcción de una tabla de dimensiones nxM y por tanto su
tiempo de ejecución es de orden de complejidad O(nM). La función Max2 es la que calcula
el máximo de dos valores.
Si además del valor de la solución óptima se desea conocer los elementos que son
introducidos, es decir, la composición de la mochila, es necesario añadir al algoritmo la
construcción de una tabla de valores lógicos que indique para cada valor E[i,j] si el
elemento i forma parte de la solución para la capacidad j o no:
Por otra parte, es necesario construir un algoritmo que interprete los valores de esta tabla
para componer la solución. Esto se realizará recorriéndola en sentido inverso desde los
valores i = n, j = M hasta i = 0, j = 0, mediante el siguiente algoritmo:
EL ALGORITMO DE WARSHALL
Al igual que ocurre con el algoritmo de Floyd descrito en el apartado anterior, estamos
interesados en encontrar caminos entre cada dos vértices de un grafo. Sin embargo, aquí
no nos importa su longitud, sino sólo su existencia. Por tanto, lo que deseamos es diseñar
un algoritmo que permita conocer si dos vértices de un grafo están conectados o no, lo que
nos llevaría al cierre transitivo del grafo.
Solución
Para un grafo g = (V,A) cuya matriz de adyacencia sea L, el algoritmo pedido puede
ser implementado como sigue:
Tras la ejecución del algoritmo, la solución se encuentra en la matriz D, que verifica que
D[i,j] = TRUE si y sólo si existe un camino entre los vértices i y j. Obsérvese la similitud entre
este algoritmo y el de Floyd. En cuanto a su complejidad, podemos afirmar que es de orden
O(n3) debido al triple bucle anidado que posee, en cuyo interior sólo se realizan
operaciones constantes.
EL ALGORITMO DE FLOYD
Sea g un grafo dirigido y ponderado. Para calcular el menor de los caminos mínimos
entre dos vértices cualesquiera del grafo, podemos aplicar el algoritmo de Dijkstra a todos
los pares posibles y calcular su mínimo, o bien aplicamos el siguiente algoritmo (Floyd) que,
dada la matriz L de adyacencia del grafo g, calcula una matriz D con la longitud del camino
mínimo que une cada par de vértices:
Solución
Tal ecuación queda resuelta mediante el algoritmo presentado que, siguiendo el esquema
de la Programación Dinámica, utiliza una matriz para evitar la repetición de los cálculos.
Con ello consigue que su complejidad temporal sea de orden O(n3) debido al triple bucle
anidado en cuyo interior hay tan sólo operaciones constantes.
EL ALGORITMO DE DIJKSTRA
Solución
Supongamos que se trata de seleccionar la ruta más corta entre dos ciudades. La red de la figura
1 muestra las rutas posibles entre el inicio en el nodo 1 y el destino en el nodo 7. Las rutas pasan
por ciudades intermedias, representadas por los nodos 2 a 6. Este problema se puede resolver
enumerando en forma detallada todas las rutas entre los nodos 1 y 7 (hay cinco). Sin embargo,
en una red grande, la enumeración exhaustiva no se puede manejar de manera computacional.
Figura 1
Figura 2
Ahora indicaremos cómo se pueden expresar matemáticamente los cálculos recursivos. Sea fi
1xi2 la distancia más corta hasta el nodo xi en la etapa i, y defínase d1xi-1, xi2 como ladistancia
del nodo xi-1 hasta el nodo xi; entonces se calcula, f1 a partir fi-1 de con la siguiente ecuación
recursiva:
Al comenzar en i = 1, la recursión pone f0 (x0) = 0 . La ecuación indica que las distancias más
cortas f1(x1) en la etapa i se deben expresar en función del siguiente nodo, xi . En la terminología
de la programación dinámica, a xi se le llama estado del sistema en la etapa i. De hecho, se
considera que el estado del sistema en la etapa i es la información que enlaza, conecta o vincula
las etapas, de tal modo que se puedan tomar las decisiones para las etapas restantes sin volver a
examinar cómo se llegó a las decisiones de las etapas anteriores. La definición correcta de estado
permite considerar por separado cada estado, y garantiza que la solución sea factible para todos
los estados. La definición de estado conduce al siguiente marco unificador de la programación
dinámica.
2) PROBLEMA DE LA MOCHILA
El modelo clásico de la mochila tiene que ver con el caso de un soldado (o un montañista) que
debe decidir cuáles son los artículos más valiosos que debe llevar en su mochila. Este problema
parafrasea un modelo general de asignación de recursos en el que un solo recurso limitado se
asigna a varias alternativas (por ejemplo, fondos limitados asignados a proyectos) con objeto de
maximizar el ingreso total. Antes de presentar el modelo de programación dinámica, haremos
notar que en las publicaciones,
al problema de la mochila también se le llama problema de conjunto de fuga o equipo de vuelo,
en el que un piloto de un avión a reacción debe determinar los artículos más valiosos (de
emergencia) que debe llevar a bordo, y el problema de carga de flete o del contenedor, en el que
un barco con capacidad limitada de volumen o peso se carga con los fletes más valiosos. ¡Parece
que estos tres nombres se acuñaron en las tres ramas de las fuerzas armadas estadounidenses:
la fuerza aérea, el ejército y la marina! La ecuación recursiva (en reversa) se desarrolla para el
problema general de una mochila de W libras, con n artículos. Sea mi la cantidad de unidades del
artículo i en la mochila, y defínanse ri y wi como el ingreso y el peso por unidad del artículo i. El
problema general se representa con el siguiente programa lineal entero:
sujeta a
w1m1 + w2m2 + p + wnmn ≤W
m1, m2, p , mn ≥0 y enteros
Paso 2
3) PROBLEMA DE LA DILIGENCIA
Este problema está referido a encontrar la ruta óptima (ruta mínima o ruta más confiable),
para viajar desde un punto llamado nodo inicial o fuente hacia otro llamado nodo final o
destino a través de una red de caminos que los conecta dados los retornos (costos beneficios
tiempo, etc.), asociados con los arcos o ramas de la red.
En este caso lo primero que hay que reconocer es, que al tomar una decisión para decidir
que ruta seguir en forma general para todo el problema, involucra analizar toda la red esto
hace que el problema sea complejo, por cuanto del nodo 1 al 14 existen múltiples alternativas
en las que se encuentran varias ramas en la ruta. En segundo lugar, ver si es posible dividir el
problema en subproblemas o etapas de manera que la decisión en cada subproblema sea
más fácil; en este sentido se ha dividió el problema en 5 etapas, de la 1 a la 5 conforme se
aprecia en la parte inferior de la red. Obsérvese que es más fácil tomar una decisión en cada
etapa pues la decisión será directa; por ejemplo si estamos en la etapa 3 (subproblema 3) al
inicio nos podemos encontrar en los nodos 5, 6 ó 7 y podemos ir a los nodos 8, 9, 10 u 11 la
decisión es inmediata, pues solo existe una rama en cada alternativa. Esta característica es
importante reconocer para tomar la decisión de aplicar la metodología de la programación
dinámica.
Ahora es posible usar la función general de la programación dinámica y luego representar esa
función en términos del problema como sigue:
Función Recursiva
General de PD.
Función Recursiva
para el Problema
Donde:
Representa la distancia por tomar la decisión Dn en la etapa n,
para pasar del estado Sn al estado Sn+1.
Nota: debe notar que tanto Sn, como Sn+1 toman un conjunto de
valores, por ejemplo S3 toma los valores:(5, 6 y 7) y S3+1 = S4,
toma los valores (8, 9, 10 y 11).
Para n=3
Para n=2
Para n=1
¿Cómo se obtiene el recorrido más corto?
La distancia total recorrida se obtiene sumando las distancias de las ramas involucradas en el
recorrido en este caso es: 1 – 3 – 5 – 9 – 12 – 14 Î 2 + 8 + 2 + 3 + 4 = 19.