Librería de programación dinámica
Texto completo
(2) LIBRERIA DE PROGRAMACION DINAMICA. Aprobado por:. Rafael Garcı́a, Asesor Fecha de Aprobación.
(3) A mi familia, quienes me han brindado todo su apoyo y aquellas personas allegadas que siempre han estado, en las buenas y en las malas.. iii.
(4) Prefacio. La programación dinámica es una de las metodologı́as implementadas para solucionar una gran variedad de problemas. Esta técnica, si se utiliza en su forma más pura, ofrece algoritmos de solución que, dada su naturaleza recursiva, tiene complejidad temporal exponencial en la mayorı́a de los casos. La metodologı́a para encontrar soluciones a este tipo de problemas, propuesta por Bohórquez[4], muestra que definiendo estructuras de datos adicionales que almacenen las soluciones, se evita volver a realizar cáculos correspondientes a subproblemas, ya que las soluciones pueden ser llevados acumulativamente; también se evita realizar cálculos innecesarios, al tratar de calcular una solución a un subproblema, ya antes solucionado, literalmente se cambia espacio por tiempo. Cabe nombrar que esta librerı́a busca ser una herramienta que estandarice el proceso de modelaje de problemas de programación dinámica, ası́ que ingresando datos del problema en las funciones de la librerı́a, se podrı́a encontrar una solución en un tiempo polinomial. Por esto se ve la necesidad y la viabilidad de la implementación de un API, que ubica al usuario en un mundo limitado a la inserción de datos que son requeridos y con estos se encontrarı́a la solución al problema de programación dinámica, esto se hace instanciando a la clase DynamicSolver, para luego darle los datos correspondientes al problema, de esta forma, la solución es alcanzada sin que el usuario sepa la implementación de alto nivel que utiliza memorización y división en subproblemas. La librerı́a exige datos basados en un problema bien modelado, para satisfacer todas las funciones del API, que logren una solución óptima, de no cumplirse se llegarı́a a soluciones incorrectas o infactibilidades de realización.. iv.
(5) Este proyecto contribuye con un proceso de aprendizaje y especialización, mezclando áreas de investigación de operaciones de ingenierı́a industrial y de diseño de algoritmos de ingenierı́a de sistemas y computación. El proyecto cuenta con el lanzamiento de un API de JAVA, que cambia la forma de hallar la solución a cualquier problema de programación dinámica, ya que apoyados a este, simplemente se plantea, modela e ingresa los datos del problema que esta limitados por los parámetros de los métodos de la librerı́a. Para tener una lectura amena de esta tesis, a continuación se explicará los temas que contiene cada capı́tulo de este documento. Inicialmente en el prefacio, se encuentra una introducción sobre este desarrollo, un breve ¿qué?, ¿para qué?, ¿por qué?, y ¿cómo leer la tesis?; luego en el capı́tulo uno, está una introducción teórica acerca de la programación dinámica, que problemas soluciona, la existencia de metodologı́as para atacar estos problemas, además de varias generalidades sobre conceptos básicos, es decir, un diccionario simple, que para lectores con pocos conocimientos del tema, clarifica ideas básicas; un capı́tulo importante es el número dos, donde el modelo y metodologı́a implementados se ven explı́citos, además muestra la estructura base para la realización del proyecto, es decir, todo el ciclo de diseño del API; en el capı́tulo tres se hace un paralelo entre la metodologı́a mencionada en 1.5 y el ¿cómo se implementó el proyecto?, explicando las estructuras de datos y la funciones que este posee, acorde con la metodologı́a utilizada; y por último, en el cuarto capı́tulo se encuentran las conclusiones y desarrollos futuros de la tesis, para que esta librerı́a abarque aun más problemas de programación dinámica. Para finalizar la inducción a la lectura de este documento, se anexa un manual que explica cómo el problema del morral es solucionado ingresando los datos que la librerı́a exige, este problema es uno de los ejemplos más famosos de la programación dinámica. En los anexos existe una investigación y conclusiones sobre que variables se necesita tener para poder desarrollar un problema de programación dinámica, donde la conclusión fué una validación al modelo usado en la tésis. Adicional al documento, se encuentra un disco compacto que contiene la librerı́a y los javadoc que muestran la documentación interna del proyecto.. v.
(6) Reconocimientos. A Rafael Garcia, por su colaboración, paciencia y ánimo, en el desarrollo de este API. A Germán Riaño, por motivar el interés en esta área del conocimiento.. vi.
(7) Tabla de Contenido Dedicatoria. III. Prefacio. IV. Reconocimientos. VI. I.. Introducción a la programación dinámica. 1. 1.1. Definición. 1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 1.2. Enfoques de la programación dinámica. . . . . . . . . . . . . . . . .. 2. 1.3. ¿Qué se puede solucionar con esta metodologı́a? . . . . . . . . . . .. 3. 1.4. Marco teórico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 4. 1.5. Metodologı́a aplicada de programación dinámica . . . . . . . . . . .. 5. II. Modelaje y diseño del problema. 7. 2.1. Elementos básicos de los problemas de programación dinámica . . .. 7. 2.2. Planteamiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 9. 2.3. El problema del morral . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.4. Planteamiento del morral bajo la metodologı́a . . . . . . . . . . . . . 11 2.5. Diseño de la implementación . . . . . . . . . . . . . . . . . . . . . . 17 2.6. Requerimientos funcionales . . . . . . . . . . . . . . . . . . . . . . . 17 2.7. Requerimientos no funcionales . . . . . . . . . . . . . . . . . . . . . 19 2.8. Casos de uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 2.8.1.. Usuario con pocos conocimientos en modelar un problema de programación dinámica . . . . . . . . . . . . . . . . . . . 20. 2.8.2.. Usuario con altos conocimientos en modelar un problema de programación dinámica . . . . . . . . . . . . . . . . . . . . . 20. vii.
(8) 2.9. Diagrama de Clases . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 III. Desarrollo de la librerı́a. 22. 3.1. Generalidades del Desarrollo . . . . . . . . . . . . . . . . . . . . . . 22 3.2. Componentes del Problema . . . . . . . . . . . . . . . . . . . . . . . 23 3.3. Estructura de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 3.3.1. Los estados . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 3.3.2. Pregunta y etapas . . . . . . . . . . . . . . . . . . . . . . . . 23 3.4. API para problemas de programación dinámica . . . . . . . . . . . . 24 3.4.1. Conjunto de estados . . . . . . . . . . . . . . . . . . . . . . . 24 3.4.2. Pregunta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 3.4.3. Condiciones inductivas . . . . . . . . . . . . . . . . . . . . . 24 3.4.4. Operador de la función Objetivo . . . . . . . . . . . . . . . . 25 3.4.5. Función aplicada a estados sucesores . . . . . . . . . . . . . . 25 3.4.6. Función de valor en el caso base . . . . . . . . . . . . . . . . 26 3.4.7. Función de valor en el caso inductivo . . . . . . . . . . . . . 26 3.4.8. Memorización . . . . . . . . . . . . . . . . . . . . . . . . . . 26 3.4.9. Librerı́a JEP . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 IV. Conclusiones y futuros desarrollos. 28. 4.1. Conclusiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 4.2. Futuros avances . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 Apéndice A.. — Manual Operativo sobre el problema del morral. 30. Apéndice B.. — Investigación previa. 34. Referencias. 37. viii.
(9) Capı́tulo I Introducción a la programación dinámica 1.1.. Definición. Programación dinámica es un método utilizado para reducir la complejidad o tiempo de ejecución de los algoritmos, esto mediante la utilización de problemas superpuestos y subestructuras adicionales. el matemático Richard Bellman fue la primera persona en plantear la ecuación base para la programación dinámica, ver Bellman[2]. Aunque el término contiene la palabra programación, dista mucho de lo que en realidad implica1 y esto es resolver problemas donde se calcula la mejor solución consecutivamente. Pero, ¿qué es una subestructura adicional? El término indica que soluciones óptimas de subproblemas, pueden ser usadas para encontrar soluciones óptimas del problema total. Por definición se puede resolver problemas con subestructuras adicionales mediante los siguientes pasos: 1. Dividiendo el problema en subproblemas mas pequeños 2. Encontrando la solución óptima de estos subproblemas usando este mismo proceso de tres pasos 2 , de forma recursiva. 3. Construir la solución óptima del problema con las soluciones de los subproblemas. 1 2. Esta palabra no hace referencia a programación como tal, pero si a programación matemática. Al proceso de tres pasos se conoce como dividir y vencer o dividir y conquistar.. 1.
(10) Los subproblemas se resuelven a su vez, con subproblemas que surgen de estos mismos, ası́ se van haciendo más sencillos de solucionar hasta llegar al caso base, donde la solución es trivial Cormen[6]. Se debe notar que una mala implementación de programación dinámica puede incurrir en desperdicios de tiempo. Pues podrı́a volver a calcular soluciones a subproblemas que ya se saben de antemano. Para solucionar este detalle, y bajar la complejidad de un algoritmo de programación dinámica, guardando las soluciones que ya se han calculado. Esta metodologı́a se conoce como memorización. A lo largo del documento se hablará del problema del morral, con el fin de hallar similitudes entre la teorı́a y la práctica, haciendo más fácil la lectura y el entendimiento de este documento. En breve, este problema, sugiere un morral que cuenta con cierta capacidad, además se cuentan con objetos que se pueden meter dentro del morral, cada uno con un costo de capacidad y un valor o beneficio al cargarlo en el morral. Se pretende encontrar la combinación de objetos que maximicen la utilidad generada en conjunto entre los objetos incluidos en el morral, simplemente teniendo bajo restricción que estos objetos quepan.. 1.2.. Enfoques de la programación dinámica De arriba a abajo. El problema se empieza a dividir en subproblemas, se solucionan los que no han sido resueltos, a medida que se divide en subproblemas y se recuerdan los que han sido resueltos, combinando inducción y memorización3 . De abajo a arriba Primero se solucionan absolutamente todos los problemas que se requieran para la implementación de antemano y luego, ya teniendo sus soluciones, estas son utilizadas para resolver a los problemas mayores. Es poco intuitivo, pero ahorra espacio y llamados a funciones. 3. Nota: Este fue el método utilizado para la implementación de la librerı́a, ya que es el más intuitivo.. 2.
(11) 1.3.. ¿Qué se puede solucionar con esta metodologı́a?. Algunos de los problemas que se pueden resolver son los siguientes: 1. Problema de cáculo de sucesión Fibonacci: La forma para ser calculado es netamente inductiva, el valor de este cálculo, es la suma de los dos últimos resultados teniendo como caso base, la función evaluada en cero o en uno, que genera como resultado uno. 2. Problema de inventario: Es considerado un problema de ordenar una cantidad de cierto tipo de items para cada N periodos, hasta encontrar los montos necesarios para suplir la demanda, ya obtenida de manera estocástica o determinista, ver Bertsekas[3]. Este problema contiene, ci , que es el inventario disponible en el periodo i-ésimo, cpi un inventario pedido en el comienzo de cada i-ésimo periodo y un di que es la demanda durante el i-ésimo periodo, dada por la distribución de probabilidad. Entonces ci+1 = ci + cpi + di , de aquı́ surgen: Un costo de oportunidad o(ci ), que es una penalidad por tener exceso, el costo de hacer el pedido por unidad C(cpi ) y M (cN ) el costo de mantener un inventario al final de los N periodos entonces el costo total, esta descrito. E{M (cN ) +. PN −1 i=0. C(cpi ) + o(ci )}. La solución encontrar el conjunto de objetos que en la etapa N , minimice mi costo. 3. Ruta más corta: Se conoce como al problema de encontrar la mejor ruta entre un vértice y los demás vértices de un grafo conecto ver Ahuja[1]. La forma de resolver este ejemplo es segmentado, si se encuentran las rutas más cortas entre los puntos, y se va analizando que ocurre si se ingresa más puntos, luego de desarrollar todos los posibles caminos a los puntos, con ayuda de las soluciones parciales se puede generar una solución definitiva.. 3.
(12) Los anteriores son algunos de los problemas que puede solucionar esta metodologı́a. Algunos cuentan con caraterı́sticas similares y pueden estar contenidos dentro de otros, ya mencionados. Este documento se apoya, en el problema del morral que es uno de estos problemas de programación dinámica. Un problema en donde la técnica de programación dividir y vencer, funciona. En general, cualquier problema que su solución pueda ser hallada partiendo el problema en subproblemas y que las soluciones de los subproblemas sirvan para hallar la solución del problema global,si el problema no llega a desbordar la capacidad de la memoria, esta metodologı́a en teorı́a lograrı́a resolverlo.. 1.4.. Marco teórico. Estado Componente de una red o grafo, es un punto, nodo o vértice, el cual diferencia una posible situación, y está definido por un nombre y una función de valor. El conjunto de estados de G se denota como V(G), y donde el orden de un grafo es el numero de estas | V (G) |. En el caso del morral, los estados constituyen a los objetos que pueden estar dentro del morral. Acción Es una decisión posible que se puede tomar en cada estado. Asocian a un estado con otro estado, también conocidos como arcos o transiciones. El conjunto de arcos de G se denota E(G) y donde el tamaño de un grafo es el numero de arcos. | E(G) |. El grado de un nodo es la cantidad de arcos que salen de este. En el ejemplo del morral los estados se conectan con los otros estados, de acuerdo a la función de sucesores que el usuario implemente. Red Es un conjunto de estados, relacionados entre si por acciones y se denota como G := (V,E), esto en el caso de no ser dirigido. En el caso de ser dirigido, E, es cambiado por un conjunto de parejas ordenadas, que indican el estado inicial y cual estado destino. Árbol Es un grafo simple conectado y acı́clico. Se le conoce como hojas a los nodos con grado 1. En este caso cada nodo, corresponde a un estado, los estados que provienen del mismo nodo hermanos, se encuentran en la misma etapa, es decir, que cada piso que tenga el árbol corresponde a otra etapa, en el caso del morral, los estados que 4.
(13) se encuentran en la misma etapa, son objetos que caben dentro del morral sabiendo que ya hay o no, otros dentro. Programación dinámica La programación dinámica es una técnica de solución de problemas, pero cuya implementación directa es ineficiente, por eso, se plantea la definición de estructuras de datos que almacenen cálculos, para evitar cálculos repetidos e innecesarios. Literalmente cambiando espacio por tiempo. Si el algoritmo que se mejora es iterativo, se acelerarı́a manejando una invariante, minimizando el uso de mucho más cálculos en la implementación del programa.. 1.5.. Metodologı́a aplicada de programación dinámica. Como funciona esto: 1. Se define un lenguaje para establecer formalmente el problema. Se incluye notación para la función f, cuyo cáculo es un valor especifico x0 , corresponda a la solución del problema 2. Se establece una recurrencia que defina la función f en un dominio que incluya a x0 . 3. Se estudia la recurrencia, determinando un diagrama de necesidades, para cada elemento x del dominio de f, establecer para que elementos se deberá conocer el valor de f para poder calcular f(x) 4. El diagrama de necesidades sugiere un invariante para un ciclo que calcule todos los valores previos necesarios para evaluar f(x0 ). Además de un orden de evaluación para los elementos del dominio de f. De esta forma se puede disminuir la complejidad temporal considerablemente, apoyando el tan conocido, dividir y conquistar. Por otra parte, ya que hay cálculos almacenados en la estructura de datos, y existe un orden de evaluación, se puede apoyar en los cálculos ya realizados y obtener los nuevos valores, de igual forma también guardar los datos ya obtenidos para que no sean vueltos a calcular. 5.
(14) Al crear una librerı́a para resolver problemas de programación dinámica, se debe recurrir a la generalización de los problemas dinámicos, separando por niveles, los datos y librerı́a; con esta separación se trataron varios problemas para analizar, cuales son los datos necesarios para desarrollar problemas de esta ı́ndole. Para entender un poco más esta técnica de solución se debe ver el planteamiento del problema del morral bajo la misma metodologı́a, ver 2.4.. 6.
(15) Capı́tulo II Modelaje y diseño del problema 2.1.. Elementos básicos de los problemas de programación dinámica. 1. El problema puede dividirse en etapas, con una decisión de la polı́tica requerida en cada etapa. En el caso del morral se divide en varias etapas que están asociadas a la capacidad restante. Es decir, si tengo una capacidad N y un costo por objeto i ci , la siguiente etapa esta definida, (N −ci ) y la decisión es, en este caso, la elegir que objeto maximiza el beneficio para esa etapa. Además existe problemas de programación dinámica que requieren análogamente de tomar sucesión de decisiones interrelacionadas y en otros, la toma de decisiones no se encuentra relacionada. 2. Cada etapa tiene un número de estados asociados a esta. Los estados asociados con cada etapa en el problema del morral, son los objetos que se quieren meter al morral, los cuales tienen además un valor vi , si ese costo ci cabe dentro del costo libre para esa etapa, el objeto o estado esta asociado a esa etapa t. En general, los estados son las diversas condiciones posibles en las que el sistema podrı́a estar en esa etapa del problema, estos pueden ser finitos o infinitos. 3. El efecto de la decisión de una polı́tica en cada etapa es; transformar el estado actual en un estado asociado con la etapa siguiente. En el ejemplo del morral, se tiene una capacidad y esta es disminuida con el costo de ingresar un objeto en el morral, se soluciona ese subproblema y queda una capacidad restante, que es el nuevo espacio disponible en el morral para ingresar objetos i, y sobre. 7.
(16) esta se vuelve a realizar el mismo proceso, hasta llegar al caso trivial, que para este seria que no quepa más objetos i, en el morral. Con los valores obtenidos por cada etapa se empieza a armar una contribución a la función objetivo en este caso, metiendo en el morral la combinación de objetos que maximicen el valor del conjunto de objetos. 4. Dado el estado actual, una polı́tica óptima para las etapas restantes. En el ejemplo del morral, Dado un objeto i que se desea evaluar para ingresar dentro del morral, desde ese punto en adelante es independiente de como llegó allı́. Para los problemas de programación dinámica en general, el conocimiento del estado actual del sistema comunica toda la información de su comportamiento previo, necesaria para determinar la polı́tica óptima de allı́ en adelante. Esta propiedad se conoce como el principio de optimalidad. 5. El procedimiento de solución empieza por hallar la polı́tca óptima para cada estado de la última etapa. Comúnmente, la solución de este problema para la última etapa es trivial. En el caso del morral, se llega al caso trivial cuando de esa etapa no se pueden generar más etapas, y eso ocurre cuando no existe capacidad restante para insertar cualquier otro objeto. 6. Se dispone de una relación recursiva que identifica la polı́tica óptima para cada estado en la etapa n, dada la polı́tica óptima para cada estado en la etapa (n+1) ∗ fn∗ (s) = máxxn (vs|xn + fn−1 (xn )). Por lo tanto, hallar la polı́tica óptima cuando se parte del estado s, quiere que se encuentre el valor que maximice xn . Esta polı́tica consiste en usar este valor de xn , seguir la polı́tica óptima cuando se parte del estado xn en la etapa (n-1). La forma precisa de la relación recursiva difiere algo entre los problemas de programación dinḿica. Ası́ sea, variable o vector xn , la variable de decisión en la etapa n. Sea fn (s, xn ), el valor que máximiza la función objetivo. dado que el sistema parte del estado s en la etapa n y se selecciona xn . Ası́ que 8.
(17) ∗ para el problema del morral, fn (s, xn ) = vs|xn + fn−1 (xn ), siendo fn∗ (s) el valor. máximo de fn (s, xn ) sobre todos los valores posibles de xn ; entonces la relación recursiva siempre sera de la forma: fn∗ (s) = máx fn (s, xn ), en donde se escribirı́a fn (s, xn ), en términos de s, xn , fn∗ y de alguna medida de la primera etapa de la efectividad o no efectividad de xn 7. Usando esta relación recursiva, el procedimiento de solución se mueve hacia atrás, etapa por etapa, hallando la polı́tica óptima, para cada estado de esa etapa, hasta que se encuentra la polı́tica óptima cuando se parte de la etapa inicial, Eso se demostró en el problema del morral, en el que se encontró la polı́tica óptima, para todos los estados y para todas las etapas. Para hacer análisis, todos los problemas de programación dinámica, se puede tener una tabla que contiene s, fn∗ (s) y xn , cuando se obtiene esta tabla para la etapa inicial, se resuelve el problema. Ya que se conocerı́a el estado inicial, la decisión inicial especificada por x∗1 . Entonces, a su vez se especifica el valor óptimo de las otras variables de decisión mediante otras tablas, de acuerdo con el estado del sistema que resulta de la decisiones precedentes. ver Hillier[5].. 2.2.. Planteamiento. Para la metodologı́a usada en este proyecto, un problema de programación dinámica requiere de: 1. Una función objetivo f que tiene un operador, el cual busca encontrar la solución óptima para ese problema, el resultado de esa función es generado inductivamente, y a ese se nombra valor inductivo. En el caso del morral se busca maximizar el beneficio llenando el morral con la mayor cantidad de objetos que generen más valor conjunto, ver 2.3. 2. Una pregunta P, que en realidad es la evaluación de una función asociada a la función objetivo, a la polı́tica alcanzada, en algún estado o valor entero. Sobre el cual se desea concluir. En el caso del morral la pregunta es cual es el. 9.
(18) conjunto de objetos que maximizan la función objetivo dado que se dispone de una capacidad C. 3. Los datos del problema: Estados Estos son los estados se pueden describir como el conjunto de opciones con los cuales cuenta el problema en cada etapa, a los estados que no tienen predecesores se les conoce como estados iniciales, y de estos surgen todos los estados de la siguientes etapas, y los estados que no tienen sucesores se conocen como estados finales. Caso Base Cuando el problema se descompone en subproblemas, ası́ sucesivamente has que se intenta resolver el caso trivial, luego de este, no hay más etapas posibles, por eso, este arroja una solución. Caso Inductivo El problema se divide en subproblemas ayudado por la condición que genera los estados sucesores, para que luego el caso base o nuevamente los casos inductivos retornen valores ya acumulados, que hacen parte de la polı́tica óptima. y el beneficio va a ser parte de la función objetivo. Función suc:, encontrar sucesores Esta función notada como h, se encuentra asociada a cada caso inductivo, ya que los estados sucesores son manipulados para generar nuevos sucesores’, cada sucesores se encuentran en otra etapa diferente a la etapa de sus predecesores. Y entre estos compiten para maximizar la función objetivo. Función h, para el caso base Esta funcioń en el caso base, genera el comportamiento de la función de valor en el caso base o caso trivial. Función h, para el caso inductivo Esta función indica el comportamiento de la función de valor mientras se va desarrollando la inducción.. 2.3.. El problema del morral. El problema del morral es uno de estos problemas de programación dinámica, donde unos objetos i ∀1≤i≤n , que se desean ingresar dentro del morral con capacidad 10.
(19) P , estos objetos i tienen un costo de capacidad pi , una cantidad de objetos oi y un valor o beneficio ui , se busca llenar el morral con la cantidad objetos óptima, es decir,. n X. pi oi ≤ C. i=0. cuantos objetos caben en el morral y máx f(oi , ui ). , la función objetivo f, depende de conjunto de elementos que se encuentran en el morral y el valor que estos en conjunto generan; en otras palabras, encontrar la combinación de objetos que maximicen el beneficio dado por la función objetivo. Donde la pregunta para este caso seria maximizar la función objetivo, para una capacidad P ya dada.. 2.4.. Planteamiento del morral bajo la metodologı́a. En esta sección, se muestra el problema del morral, utilizando la técnica de solución de problemas de programación dinámica dividir y vencer, tomado de Bohórquez[4]. El problema del morral es un problema clásico de la investigación de operaciones. Una solución recurrente ingenua puede resultar demasiado onerosa. La solución que se presenta, utilizando programación dinámica, es eficiente y práctica1 . El problema: Dados n objetos, o1 , ..., on Para i = 1, ..., n, pi es el peso de oi . Naturalmente, pi > 0 Un morral, que soporta un peso máximo P . 1. A pesar de ser un problema NP-completo, la solución que se presenta es eficiente en la praxis, si se concede que los números involucrados no sean arbitrariamente grandes. 11.
(20) Para i = 1, ..., n, si se carga en el morral el objeto oi , se obtiene un beneficio o utilidad ui . Se quiere maximizar la utilidad de cargar en el morral algunos objetos. El lenguaje ut(j,x) : Utilidad máxima que se consigue, si los objetos que se pueden cargar se eligen entre o1 ,...,oj , si el peso máximo que se puede soportar es x. ut(n,P) : Respuesta deseada. La recurrencia ut(j,x)= 0 , si j=0 =. ut(j-1,x), si 1 ≤ j ≤ n, 0 ≤ x< pj. =. máx{ut(j-1,x), ut(j-1,x-pj ) + uj }, si 1 ≤ j ≤ n, 0 < x≤ pj. El diagrama de necesidades. Figura 1: Diagrama de necesidades El algoritmo. 12.
(21) Ası́: T (n, P ) = O(nP ) S(n, P ) = nP Mejoras: El algoritmo puede mejorarse, si se observa con mas cuidado el diagrama de necesidades: basta guardar únicamente las dos últimas columnas calculadas: Ası́:. 13.
(22) T (n, P ) = O(nP ) S(n, P ) = 2P El ahorro en espacio es significativo. La situación puede, incluso, mejorar hasta lograr que se deba almacenar solo una columna, si se piensa que se puede mantener un invariante que afirme:. 14.
(23) El nuevo algoritmo puede escribirse de la siguiente forma:. Hasta aquı́, se ha solucionado el problema de conocer cuál serı́a la utilidad máxima posible. En la práctica se quiere averiguar, además, en que forma se puede alcanzar este óptimo. Ahora, ¿cómo saber que objetos se llevan? La técnica se extiende para considerar una función adicional que permita recordar las decisiones que se tomaron en el cálculo del óptimo. Los valores de esta función deben almacenarse para construir la respuesta a la nueva pregunta. Para este caso: 15.
(24) sel(j,x) : 1 si oj se lleva, cuando el peso máximo permitido es x, : 0 si oj no se lleva, cuando el peso máximo permitido es x. Nótese que: sel(j,x) = 1 ⇔ 0 < j ≤ P ∧ x ≥ pj cand ut(j-1,x-pj )+uj Con esta definición, el algoritmo puede modificarse para calcular sel :. Se debe observar que la estructura del algoritmo que calcula el óptimo se respeta y se utiliza como plataforma sobre la que se puede, adicionalmente, escribir código que permita recordar las decisiones tomadas.. 16.
(25) Finalmente, para determinar que objetos se pueden llevar para conseguir la utilidad máxima, defı́nase, para 1 ≤ i ≤ n : x[i]= 1 ⇔ oi se lleva, para alcanzar el óptimo. El siguiente algoritmo, que calcula en el arreglo X[1..n] el vector x[1..n]:. 2.5.. Diseño de la implementación. A continuación se encuentra el ciclo de diseño de la aplicación y se encuentra dividido en: Levantamiento de requerimientos, el cual consta de las cosas que esta aplicación debe hacer y se encuentra segmentado en requerimientos funcionales, propios de la aplicación para que esta cumpla con su objetivo y requerimientos no funcionales, que son parte de la calidad del programa; luego los posibles casos de uso, explicado para cada usuario que pueda usar la librerı́a; por último un diagrama de clases, el cual contiene el conjunto de estructuras de datos, separadas por objetos, con sus respectivos atributos o variables y sus métodos, que son aquellas funciones que sirven para interactuar entre objetos.. 2.6.. Requerimientos funcionales. 1. Hallar el escenario óptimo, o conjunto de escenarios óptimos por etapa, de un problema bien modelado. 17.
(26) 2. Tendrá la capacidad de amoldarse ante cualquier tipo de problema de programación dinámica con que el usuario se encuentre. Y encontrar una solución óptima, factible, etc., todo esto, dependiendo de la forma como se plantee el problema. 3. Desarrolla un esquema de estados alcanzables a partir de las condiciones inductivas presentadas. 4. Con javadoc y un manual de soporte para el API, se facilitara el entendimiento de este. 5. Los resultados serán explı́citos con el valor final de la f.o, también llamado f(x0 ) decisión asociada a cada mejor estado en cada etapa. 6. El API tendrá métodos para resolver tanto problemas determı́nisticos, esto esta sujeto a la metodologı́a del desarrollo. 7. El usuario, debe indicar claramente la función objetivo de la cual deriva su caso base y su caso inductivo, como avance al desarrollo y facilitar, el recorrido entre estados y etapas, el usuario en el planteamiento del problema podrá agregar un caso transitivo, que evite recorrer más estados y acelere el encontrar el caso base. 8. El usuario podrá agregar condiciones 9. El usuario de acuerdo a su metodologı́a podrá encontrarse con un resultado óptimo hallado de forma eficiente, un resultado óptimo encontrado de una forma no eficiente, o no alcanzar el caso base, por mal planteamiento del problema. Casos de ciclaje o infactibilidad. 10. La separación de tres niveles: datos, problema y programa; existe por lo siguiente a) Usuario es responsable de ingresar los datos como el API los exige.. 18.
(27) b) De acuerdo a la mezcla entre parámetros requeridos y opcionales, se puede atacar de diferentes formas un problema dinámico, ya que en el fondo, todos los problemas dinámicos concuerdan en varios detalles. c) De acuerdo a investigaciones soportadas por Bellman, el principio de programación dinámica es el mismo, y se encuentra basado en una ecuación sencilla, que fue la base de este desarrollo. 11. El problema no requiere definición de etapas, ya que genéricamente es atemporal, pero manejando responsablemente y con un buen modelaje los estados pueden aparecer modificando las condiciones. El control de recorrido y de etapas queda a cargo del API. 12. El usuario se hace responsable del buen modelaje del problema para una buena inserción de los datos del problema dinámico al API.. 2.7.. Requerimientos no funcionales. 1. La librerı́a implementa la técnica de programación antes mencionada como dividir y vencer, además de estructuras de datos que memoricen cálculos, para que esta sea eficiente, aumentando la respuesta en tiempo. 2. La librerı́a tiene muy bien definida en sus estructuras, el problema y los elementos de este. 3. La librerı́a debe estar bien documentada. 4. Se debe tener un manual que sirva para ilustrar como insertar datos de un ejemplo para encontrar una solución a un problema de programación dinámica. 5. Como fue mencionado este desarrollo va ser implementado en JAVA.. 19.
(28) 2.8.. Casos de uso. 2.8.1.. Usuario con pocos conocimientos en modelar un problema de programación dinámica. 1. Insertar datos requeridos, el usuario puede insertar estos datos basándose en un ejemplo ya realizado y apoyándose en un manual. 2. Este usuario encontrara soluciones al problema, al cual sencillamente modificó los datos que recibı́a el API. 2.8.2.. Usuario con altos conocimientos en modelar un problema de programación dinámica. 1. El usuario experto puede utilizar la librerı́a como una librerı́a para cualquier aplicación, no solo botando resultados sino, siendo parte de un desarrollo para toma de decisiones. 2. De acuerdo a los datos requeridos y opcionales, el usuario podrá modificar la inserción y mezclar entre estos, definiendo bien un problema 3. Agregar nuevos ejemplos con el lenguaje. Para usuarios inexpertos. 4. Insertar datos requeridos, el usuario puede insertar estos datos basándose en un ejemplo ya realizado y apoyándose en un manual. 5. Mediante un comando el usuario puede genere una solución óptima factible, de acuerdo a la metodologı́a utilizada en el programa. 6. Los resultados deben ser presentados en la consola de JAVA. 2.9.. Diagrama de Clases. 20.
(29) Figura 2: Diagrama de clases 21.
(30) Capı́tulo III Desarrollo de la librerı́a 3.1.. Generalidades del Desarrollo. Con los concepto tomados en el modelaje del problema, se puede hacer un paralelo con este capitulo, aquı́ se convierte el modelo de la solución en estructuras de datos y funciones que van acordes a la metodologı́a. Este proyecto de grado consta del desarrollo de un API (Application Programming Interface - Interfaz de Programación de Aplicaciones; un conjunto de especificaciones de comunicación entre componentes software) de programación dinámica para JAVA , basados en la metodologı́a vista en el capı́tulo dos. El API tendrá funciones que permitan crear y recorrer el conjunto posible de estados, para ser evaluados y encontrar soluciones óptimas a los problemas planteados. Los métodos son fácilmente entendibles, concisos para que el usuario ingrese los datos, paso a paso, sin mayores traumas. El punto crı́tico de este trabajo por parte del usuario, es en un buen modelaje de los problemas de programación dinámica, para que al usuario final tenga una transparencia al llamar los métodos del API. Un mal modelaje del problema incurrirá en una mala inserción de datos o resultados incoherentes, llegado a una solución errónea o simplemente inexistente. Un problema bien planteado podrı́a tener solución, dependiendo de los estados y etapas que debe inspeccionar, ya que cada estado en cierta etapa puede generar más subproblemas y si el problema esta bien modelado pero no esta acotado igualmente no tiene solución por este método, al final, el ahorro es en tiempo.. 22.
(31) 3.2.. Componentes del Problema. Se analiza que los estados, deben guardar la información que es acumulada, para luego que sea comparada entre estos y ver cual es el estado óptimo y avanzar a la siguiente etapa. Además el usuario puede hacer referencia a lo que le interese. Un estado contará, con un apuntador a lo que el usuario desee. Contará con un valor interno si es necesario, que sera asociado por el algoritmo, de acuerdo a como se ingrese la defición del problema. Las restricciones están asociadas entre variables que se encuentran en la función objetivo y otras variables que se encuentran en el lenguaje definido por el usuario.. 3.3. 3.3.1.. Estructura de datos Los estados. Los estados contienen valores fijos y variables calculados que al estar almacenados y permanecer en una estructura de árbol, simplifican drásticamente la complejidad del algoritmo. Costo: Cada estado tiene definido su costo asociado, este costo es de tipo entero, ya que es un problema de programación dinámica y no un algoritmo greedy. Valor: Cada estado posee, un valor tipo real el cual es tenido en cuenta para calcular la polı́tica óptima y la función objetivo. Valor Inductivo: Este valor es un control y esta limitado, por la condición inductiva del problema, el cálculo es generado, si el estado donde se encuentra es un posible sucesor. Fórmula de la función de valor: Este valor es acumulativo y corresponde al valor inmediato que genera el incurrir en ese estado, más todos los valores de los estados que se obtuvieron para llegar a esa etapa. 3.3.2.. Pregunta y etapas. Para evitar volver a procesar cálculos ya realizados, se utiliza una estructura de tablas de hash anidadas, con etapas y la pregunta que evaluada para los subproblema genera un conjunto de preguntas más sencillas de responder, si en la inducción. 23.
(32) se encuentra con la misma etapa y con la pregunta que ya ha sido evaluada para el subproblema, no es necesario desarrollar los cálculos para obtener el resultado correspondiente ya que fue calculado y memorizado,en la estructura antes mencionada.. 3.4.. API para problemas de programación dinámica. Esta librerı́a debe ser invocada, con todos los estados posibles, indicando la condición inductiva y haciendo explı́cito que hacer con la función objetivo. En el apéndice, se encuentra un manual operativo basado en el ejemplo del morral, allı́ se hace un paralelo entre esta sección y el ejemplo se ha venido trabajando en este documento. 3.4.1.. Conjunto de estados. Es el primer parámetro de la librerı́a cuando se invoca el método solve(), en este caso es un Vector de objetos State, donde se indica la totalidad de estados que la librerı́a puede alcanzar o en su defecto los estados iniciales, pero indicando en la condición inductiva cuales son los posibles sucesores y en la función aplicada a los estados sucesores decir que hacer con estos. 3.4.2.. Pregunta. Es el segundo parámetro del método solve() , en este caso es un Vector de objetos Integer, donde se indica la pregunta del problema, para problemas de programación dinámica, esta pregunta es inductiva, y es divisible en subconjuntos, por eso, lo considere como un conjunto de enteros. 3.4.3.. Condiciones inductivas. Es el tercer parámetro del método solve(), 24.
(33) en este caso es un Vector de objetos Condition, donde se indica que condición se debe cumplir, manipulando los nombre de las variables que se encuentran dentro de los estados, para encontrar los sucesores a cada estado analizado, esta condicción por ende, al ser evaluada por el algoritmo que implementa esta metodologı́a debe retornar falso o verdadero. 3.4.4.. Operador de la función Objetivo. Es el cuarto parámetro del método solve(), en este caso es un String, donde se indica que tipo de operación se va a realizar con los valores resultantes en cada etapa. Es decir, si la función objetivo esta maximizando, entonces por etapas se debe encontrar el valor que maximice la función, los mismo con la minimización, sumatoria, etc. Para este caso se implementa las cadenas de caracteres MAX y MIN. 3.4.5.. Función aplicada a estados sucesores. Este parámetro es ingresado por una función que recibe un vector, por eso, primero creo el vector, luego le agrego la función en una cadena de caracteres mediante el método next.add(s:String); y luego agrego el vector a la librerı́a s.setNext(next);, antes de ser llamado el método solve(), en este caso es un Vector, de objetos String, que contiene las mismas variables de estado que manejan las condiciones inductivas, indicando el resultado a seguir de los sucesores. Esta esta directamente asociada a cada caso inductivo, de esta forma 25.
(34) cada vez que se crea una condición inductiva, se debe crear una función de este tipo para que sea aplicada a los sucesores. La forma de asociarlos es simplemente agregándolos en los vectores en el mismo orden, al final quedaran dos vectores el primero con la condición en la posición i y su función correspondiente también en la posición i. 3.4.6.. Función de valor en el caso base. Este parámetro es ingresado por una función que pertenece al API setBcVal(s:String), antes de ser llamado el método solve(), en este caso es un objeto String que contiene las mismas variables de estado que maneja la función aplicada a sucesores indicando con el resultado, que valor retorna la induccı́on al encontrarse con el caso base. 3.4.7.. Función de valor en el caso inductivo. Este parámetro es ingresado por una función que pertenece al API setValueFormula(s:String), antes de ser llamado el método solve(), en este caso es un objeto String que contiene las mismas variables de estado que maneja la función aplicada a sucesores, indicando con el resultado, que valor retorna la induccı́on al encontrarse con el caso inductivo. 3.4.8.. Memorización. Se implemento en el método inductiveCalculated() en la clase InductionClass, el cual guarda la pregunta evaluada bajo cierto valor correspondiente al subproblema, en cierta etapa. Esto se hace implementado dos niveles de tablas de Hash. Uno para la etapa y otro para la pregunta evaluada bajo las condiciones del subproblema. 26.
(35) 3.4.9.. Librerı́a JEP. Para la manipulación de condiciones, expresiones y para que el usuario desde un nivel superior las programe literalmente, sin echar codificar en JAVA las funciones, condiciones o datos, se utilizó una librerı́a libre de JAVA, que convierte cadenas de caracteres en expresiones evaluables, existen dos clases que implementan esta librerı́a JEP1 : Una llamada Condition que simplemente retorna falso o verdadero. Otra llamada Expression que retorna un valor real. Esta librerı́a es muy útil, ya que permite manejar variables, por eso, se le permite al usuario nombrarlas al invocar el API, para que esos nombres queden asociados a las variables de estado, de las cuales se habló en la sección componentes del problema en el punto correspondiente a estado. Luego de asociarlas el usuario puede en las condiciones evaluarla, y en las funciones que se aplican a los sucesores ocurrido algún caso, puede modificarlas. Sin esta librerı́a, el usuario no estarı́a separado totalmente del API que encuentra la solución a los problemas de programación dinámica.. 1. JEP es una librerı́a, que recibe una cadena de caracteres que contenga ecuaciones, expresiones, condiciones; entre otras cosas que pueden devolver un valor, permitiendo que estas sean evaluadas y arrojando un resultado, para encontrar más información sobre esta librerı́a, se debe visitar http://www.singularsys.com/jep/doc/javadoc/org/nfunk/jep/JEP.html. 27.
(36) Capı́tulo IV Conclusiones y futuros desarrollos 4.1.. Conclusiones. Este implementación es una primera versión de un API para encontrar soluciones a ciertos problemas de programación dinámica en tiempo polinomial, es un software libre para la comunidad JAVA, bastante completo en su expresión más sencilla. Es genérico y depende en una gran parte, de la forma como el usuario lo modele. Especı́ficamente en que es problema del usuario respetar que las condiciones inductivas sean excluyentes. Entre otros detalles de especificación. Se logró desarrollar una librerı́a adaptativa, con inducción enfocada de arriba hacia abajo, cumpliendo con todos los parámetros que un problema de programación dinámica requiere. Se creo un manual corto, para que el usuario con un ejemplo de problema de programación dinámica, llamado el problema del morral, pueda montar cualquier otro tipo de problema de esta ı́ndole, y encontrarle solución de igual forma. Es una implementación, basada en una metodologı́a existente, brindando otro enfoque para encontrar soluciones a problemas de programación dinámica a la comunidad de programadores en JAVA. Este desarrollo, puede ser el inicio de un grupo de investigación que trabaje sobre un buen modelaje de problemas de programación dinámica y como volver este API más intuitivo para el usuario.. 28.
(37) 4.2.. Futuros avances. Para este proyecto hay surgido un interés en desarrollar un complemento, mucho más amigable, con una buena interfaz gráfica y con un modulo de análisis estadı́stico de los datos. Para el sector asegurador. Extender este algoritmo a no determinista, asociando probabilidades para incorporar más ejemplos a este como procesos de decisión markoviana, ruta más corta estocástica, etc. Realizar mezclas entre este algoritmo con otros tipos de programación para que surjan nuevas metodologı́as de desarrollo de problemas con incertidumbre, con complejidad computacional alta, etc. Desarrollando más ejemplos, haciendo mezclas con otras metodologı́as, se puede afianzar el manual del usuario de esta librerı́a, además de investigar para que en futuras versiones sea más intuitivo el proceso de ingresar datos.. 29.
(38) Apéndice A Manual Operativo sobre el problema del morral 1. Crear una instancia del programa, de esta forma DynamicSolver s= new DynamicSolver(); 2. Crear la pregunta inicializando un objeto Vector, con las capacidades, montos, etc. Sobre los cuales se quiere saber el valor acumulativo del problema. Aquı́ se hace explı́cito un morral con capacidad de 8. Collection capacities = new Vector(); capacities.add(new Integer(8)); 3. Luego se definen los estados, dándoles un nombre, un costo entero y un valor real. para que luego sean ingresados a un conjunto de objetos llamado States State s1= new State("Helado Vainilla", 64, 100000); State s2= new State("Helado Chocolate", 32, 45000); State s3= new State("Helado Fresa" , 16, 22000); State s4= new State("Helado Ron con pasas", 8, 10000); State s5= new State("Helado Arequipe" , 4, 4500); State s6= new State("Helado Capuccino", 2, 2200 ); States sts = new States();. 30.
(39) sts.addState(s1); sts.addState(s2); sts.addState(s3); sts.addState(s4); sts.addState(s5); sts.addState(s6); 4. Se indica que operación se va a realizar sobre la función objetivo, este caso es Maximización Max.. String objop = "Max"; 5. Ahora se le da nombres a las variables que tiene el sistema para que el usuario las opere como quiera, en la función de valor tanto del caso base como de los casos inductivos, también son indispensables para la inducción, con la condición de sucesores y las operaciones que se hacen sobre estos.. String cost= "cost"; //Corresponde al nombre de la variable del costo, capacidad, etc. Fijo por estado. String indcost = "rescap"; // Corresponde al nombre de la variable asociada con el costo, capacidad de la pregunta String polval = "policyval"; // Corresponde al nombre de la variable que acumula el valor por escoger ese estado y todos los que se tomaron para llegar all String valname = "val"; // Corresponde al valor inmediato de incurrir en un estado, para cierta etapa. 31.
(40) String stateval = "sval"; // Corresponde al valor generado por el estado indicado por el usuario.. s.setInductiveCostName(indcost); s.setCostName(cost); s.setPolicyValueName(polval); s.setValueName(valname); s.setStateVariableName(stateval); 6. Se crea las condiciones inductivas, que indican como encontrar los sucesores. Apoyada en los nombres antes mencionados. En este caso se indica que los sucesores son para este caso inductivo, los que quepan dentro de la capacidad restante. Collection icond = new Vector(); icond.add("rescap - cost >= 0");. 7. Se asocia a cada condición inductiva, una función que indica que hacer con los sucesores. Apoyada en los nombres antes mencionados. En este caso, con lo sucesores, se saca la nueva capacidad restante, y la pregunta, se soluciona para un subproblema. Nota: Se debe respetar que las condiciones inductivas sean excluyentes, es decir, ya que los sucesores son generados por esta condición, estas condiciones no deben genera grupos de sucesores, cuya intersección sea diferente de vacı́o. Collection next = new Vector(); next.add("rescap - cost"); s.setNext(next); 32.
(41) 8. Se le hace explı́cito a la librerı́a la función que genera un valor a devolver cuando el algoritmo encuentre el caso base, para este caso debe devolver el valor de incurrir en ese estado. s.setBcVal(val); 9. Se le hace explı́cito a la a librerı́a la función que genera un valor a devolver cuando el algoritmo encuentre un caso inductivo dado, aquı́ se ve que el usuario le indica al algoritmo que sume al acumulado de la función de valor, el valor de incurrir en ese estado. s.setValueFormula("policyval + val");\\ 10. Por último se llama al método solve(), y se le ingresan estos párametros s.solve(sts, capacities, icond, objop); Luego de haber realizado estos pasos se corre el método solve() de la librerı́a y en consola aparecerán, el valor correspondiente a la función objetivo evaluada según los parámetros de la pregunta y el conjunto de decisiones que corresponde a la polı́tica que mejor se ajusta al problema de programación dinámica.. 33.
(42) Apéndice B Investigación previa Analice cuatro de los múltiples problemas de programación dinámica, los cuales fueron inspeccionados para encontrar similitudes y diferencias. Ya que lo que se buscaba, era crear un API que pueda solucionar cualquier problema de programación dinámica, aunque se ve una convergencia entre los datos que se requieren según esta investigación, con los datos que se propone en la metodologı́a, ver sección1.5. Los problemas analizados son algunos de los problemas dinámicos más mencionados, y de conocimiento publico fueron: 1. El del Morral1 2. El de los signos 3. Ruta más corta a) Corrección de etiquetas b) Rama más pesada en un árbol c) Ruta más larga o máx. beneficio 4. Subcadena comn entre Cadenas, tiene ligero parecido al de los signos Entre los anteriores problemas mencionados, se recorre entre posibles soluciones, encontrando por etapas cual es la mejor condición, y ası́, llegar al mejor beneficio. 1. Este es el problema por excelencia más nombrado en cuanto a programación dinámica, se puede ver más a fondo ver Bertsekas[1]. 34.
(43) La diferencia entre estos, es que calcular estados alcanzables desde un estado y una etapa, es más explı́cita unos que otros, es decir, unos calculan a donde pueden llegar y otros simplemente por asignación alcanzan sus estados alcanzables. Luego de tener estados alcanzables, se encuentra la mejor decision entre estos. Este paso es mucho más fácil que el anterior, porque se vuelve a tomar la famosa ecuación de Bellman.. B.1.. Datos genéricos. B.1.1.. Cálculos y datos por el usuario. 1. Contador de objetos i y cual es su máximo valor 2. Peso, costo o distancia p(i) esto esta asociado a un estado y una etapa. 3. Utilidad o beneficio u(i). 4. Restricciones de costo asocia p(i) con un máximo Costo, si hay Costos 5. Restricciones de utilidad asocia u(i) con un mı́nimo de Utilidad si hay Utilidades 6. Caso base relacionada con la función objetivo. Requerido 7. Caso Inductivo en función de la F.O. Requerido con condición y estos con datos asociados a las variables de decisión 8. Caso transitivo o mejoras al algoritmo, que se puede desarrollar luego de aplicar el inductivo. Para acelerar la respuesta. Opcional 9. Operador función objetivo min., máx. Requerido 10. Pregunta, es el estado para el cual se quiere evaluar función objetivo. En el caso del morral como maximizó el morral si el tamaño es 0.3 metros cúbicos. 11. Variables de decisiń, que se deben guardar por estado, como quien es mi predecesor. Si quiero saber la ruta.. 35.
(44) B.1.2.. Cálculos y datos del programa. 1. Rangos, dados por la inicialización 2. Estados marcados o estados parcialmente óptimos, dados por la inicialización y modificados por la inducción, es decir, avance y control de etapas 3. Estados posibles , dados por la inicialización y modificados por la inducción 4. Inicialización de variables, en función de otras o transformadas. Explı́citamente estados alcanzables a partir de restricciones e inducción. 5. transformar restricciones en estados alcanzables o aceptar matriz de estados alcanzables.. 36.
(45) Referencias. [1] Ravindra K. Ahuja. Network Flows, Theory, Algorithms and Applications. Number 013617549X. Prentice Hall, Englewood Cliffs, New Jersey, 1993. [2] Richard Bellman. Dynamic Programming. Princeton University Press, Princeton, N.J., 1956. [3] Dimitri P. Bertsekas. Dynamic Programming and Optimal Control, volume II. Athena Scientific, Belmont, Massachusetts, 1995. [4] Jaime Bohórquez. Diseño efectivo de programas correctos. Editorial Escuela Colombiana de Ingenierı́a, 2006. [5] Gerald J. Lieberman Frederick Hillier. Introducción a la investigación de operaciones. McGraw - Hill, 3 edition, enero 1982. traducción de la tercera edición. [6] Ronald L. Rivest Thomas Cormen, Charles E. Leiserson. Introduction to Algorithms. The MIT Press, 2001.. 37.
(46)
Documento similar
D) El equipamiento constitucional para la recepción de las Comisiones Reguladoras: a) La estructura de la administración nacional, b) La su- prema autoridad administrativa
"No porque las dos, que vinieron de Valencia, no merecieran ese favor, pues eran entrambas de tan grande espíritu […] La razón porque no vió Coronas para ellas, sería
Cedulario se inicia a mediados del siglo XVIL, por sus propias cédulas puede advertirse que no estaba totalmente conquistada la Nueva Gali- cia, ya que a fines del siglo xvn y en
El nuevo Decreto reforzaba el poder militar al asumir el Comandante General del Reino Tserclaes de Tilly todos los poderes –militar, político, económico y gubernativo–; ampliaba
Habiendo organizado un movimiento revolucionario en Valencia a principios de 1929 y persistido en las reuniones conspirativo-constitucionalistas desde entonces —cierto que a aquellas
The part I assessment is coordinated involving all MSCs and led by the RMS who prepares a draft assessment report, sends the request for information (RFI) with considerations,
De hecho, este sometimiento periódico al voto, esta decisión periódica de los electores sobre la gestión ha sido uno de los componentes teóricos más interesantes de la
Para recibir todos los números de referencia en un solo correo electrónico, es necesario que las solicitudes estén cumplimentadas y sean todos los datos válidos, incluido el