Diseño de algoritmos

Documentos relacionados
259. El número de combinaciones de m objetos entre un conjunto de n, denotado por n, para n 1 y 0 m n, se puede definir recursivamente por: m

4.2. El número de combinaciones de m objetos entre un conjunto de n, denotado por n, para n 1 y 0 m n, se puede definir recursivamente por: m

Ejemplo: El problema de la mochila. Algoritmos golosos. Algoritmos y Estructuras de Datos III. Segundo cuatrimestre 2013

Programación Dinámica 1

Algoritmos glotones. mat-151

Análisis de algoritmos

Programación Dinámica

7.1 Consideraciones. Considere la búsqueda de un libro en una biblioteca. Considere la búsqueda de un nombre en el directorio telefónico.

Introducción a la Programación Dinámica. El Problema de la Mochila

Jueves, 30 de abril. Ejemplo de recursión. Ejemplo de PD. Ejemplo de programación dinámica. Programación dinámica

1. GRAFOS : CONCEPTOS BASICOS

Teoría de grafos y optimización en redes

Técnicas de diseño de algoritmos Programación dinámica

Tema: Algoritmos para la ruta más corta en un Grafo.

Tipos algebraicos y abstractos. Algoritmos y Estructuras de Datos I. Tipos algebraicos

Números naturales y recursividad

Sistemas de Ecuaciones Lineales y Matrices

PROCESADORES DE LENGUAJE EXAMEN FINAL 8-JUNIO-07

La segunda observación permite reformular el problema de una manera más simple:

Estrategias de Diseño de Algoritmos

El TAD Grafo. El TAD Grafo

Tema 7: Problemas clásicos de Programación Lineal

Convertir un AFND a un AFD

Formulación del problema de la ruta más corta en programación lineal

El rincón de los problemas

Tema 2. Divide y vencerás.

Matrices. Ejercicio 1. Dada la matriz A = 2. completa: a 11 =, a 31 =, a 23 =, = 3, = 2, = 7.

Grafos. Suponiendo que e = [u, v]. Entonces los nodos u y v se llaman extremos de e y u y v se dice que son nodos adyacentes o vecinos.

Este material es de uso exclusivo para clase de algoritmos y estructura de datos, la información de este documento fue tomada textualmente de varios

Análisis de Algoritmos

Conjuntos y matrices. Sistemas de ecuaciones lineales

Algebra lineal y conjuntos convexos

Flujos de redes (Network Flows NF)

Estructuras de control

CAPÍTULO 6 PROGRAMACIÓN DINÁMICA. Programación Dinámica

Máquinas de Turing IIC3242. IIC3242 Máquinas de Turing 1 / 42

Estructuras de Datos y de la Información Ingeniería Técnica en Informática de Gestión. Curso 2007/2008 Ejercicios del Tema 2

Tema 3: El Método Simplex. Algoritmo de las Dos Fases.

El problema del agente viajero

Matrices 3. Matrices. Verónica Briceño V. agosto 2012

Figura 1: Esquema de las tablas simplex de inicio y general.

Escribir programas a partir de un diagrama de flujo

Algoritmos y Estructuras de Datos Curso 06/07. Ejercicios

Estructuras de Control

Fundamentos de Investigación de Operaciones Modelos de Grafos

Concepto de Recursión. Características de algoritmos recursivos. Ejemplos

Análisis y Diseño de Algoritmos

3. Qué posibilidades hay de que me toquen los cuatro ases en una mano de tute?.

La Máquina de Acceso Aleatorio (Random Access Machine)

Algoritmos para determinar Caminos Mínimos en Grafos

1. Diseñe algoritmos que permitan resolver eficientemente el problema de la mochila 0/1 para los siguientes casos:

COMPLEMENTO DEL TEÓRICO

Análisis y Complejidad de Algoritmos. Arboles Binarios. Arturo Díaz Pérez

Complejidad computacional (Análisis de Algoritmos)

ALGEBRA 1- GRUPO CIENCIAS- TURNO TARDE- Enteros

A l g o r i t m o y E s t r u c t u r a d e D a t o s Ing. en Sistemas de Información - 1º año -

Continuidad y monotonía

Aplicaciones Lineales. Diagonalización de matrices.

Cubiertas convexas II

Grafos. Amalia Duch Brown Octubre de 2007

Sistemas de ecuaciones lineales

Clase 8 Matrices Álgebra Lineal

Cálculo Diferencial: Enero 2016

Problema de las N Reinas. Resolución paralela

Recursión. Capítulo 4

TEMA 1. MATRICES, DETERMINANTES Y APLICACIÓN DE LOS DETERMINANTES. CONCEPTO DE MATRIZ. LA MATRIZ COMO EXPRESIÓN DE TABLAS Y GRAFOS.

Algoritmos genéticos

Degeneración y ciclaje. Método de las dos fases CO-3411 (S08) 30/03/

Apuntes elaborados por: Aaron Asencio, Eduardo Quevedo, Raquel López Revisado por: Javier Miranda el???

APUNTADORES. Un apuntador es un objeto que apunta a otro objeto. Es decir, una variable cuyo valor es la dirección de memoria de otra variable.

Es una ecuación polinómica de grado uno con una o varias incógnitas. Por ejemplo, son ecuaciones lineales: 2x 3y 4z

Cálculo científico y técnico con HP49g/49g+/48gII/50g Módulo 3: Aplicaciones Tema 3.4 Grafos

Metaheurísticas y heurísticas. Algoritmos y Estructuras de Datos III

PROBLEMAS RESUELTOS DE PREPARACIÓN PARA OPOSICIONES. Problemas 02

RESOLUCIÓN DE SISTEMAS LINEALES

IN Guía de Problemas Resueltos de Geometría de Programación Lineal v1.0

Elementos de Probabilidad y Estadística. Primer Examen. Parte 2

Generación de Código Intermedio

Tema 6: Resolución aproximada de sistemas de ecuaciones lineales

Colección de Problemas II. mín Z = 8x 1 + 9x 2 + 7x 3 s. a: x 1 + x 2 + x x 1 + 3x 2 + x x 1 + x 2 x 3 30

Máquinas de Turing IIC3242. IIC3242 Máquinas de Turing 1 / 45

SOLUCIÓN A LA ECUACIÓN EN DIFERENCIAS FINITAS. Hernández Cruz G. Berenice.

Ejercicios Tema 6. Funciones

Informática General 2016 Cátedra: Valeria Drelichman, Pedro Paleo, Leonardo Nadel, Norma Morales

Algebra Matricial y Teoría de Grafos

!DETERMINANTES. Tema 3.- DETERMINANTES !MATRICES EQUIVALENTES POR FILAS!RANGO DE UNA MATRIZ. APLICACIONES. Un poco de historia

NP-Completeness: Universidad Autónoma de Nuevo León Facultad de Ingeniería Mecánica y Eléctrica División de Posgrado en Ingeniería de Sistemas

Preliminares Interpolación INTERPOLACIÓN Y APROXIMACIÓN POLINOMIAL

TEMA IV TEORÍA DE GRAFOS

Lenguajes y Compiladores Análisis Sintáctico Parte I. Teoría Lenguajes 1

Retículos y Álgebras de Boole

Relaciones. Estructuras Discretas. Relaciones. Relaciones en un Conjunto. Propiedades de Relaciones en A Reflexividad

CAPÍTULO 4 PROGRAMACIÓN LINEAL ENTERA

Estructuras de Datos Orientadas a Objetos. Pseudocódigo y aplicaciones en C#.NET. Capítulo 9.- Grafos

Definición 1 Un semigrupo es un conjunto E provisto de una operación binaria asociativa sobre E, se denota por (E, ).

OPTIMIZACIÓN VECTORIAL

PREPARACION OLIMPIADA MATEMATICA CURSO

Transcripción:

Diseño de algoritmos Programación Dinámica Jesús Bermúdez de Andrés Universidad del País Vasco/Euskal Herriko Unibertsitatea (UPV/EHU) Curso 2008-09

1 Programación Dinámica Definición de la técnica Funciones con memoria Optimización con Programación dinámica Problema de la mochila con valores enteros Problema de la subsecuencia común más larga Problema de todos los caminos mínimos

Programación dinámica La técnica de Programación dinámica fue inventada como un método general de optimización de procesos de decisión por etapas. La técnica de programación dinámica es adecuada para resolver problemas cuya solución puede caracterizarse recursivamente (como con la técnica divide y vencerás) y en la que los subproblemas que aparecen en la recursión se solapan de algún modo, lo que significaría una repetición de cálculos inaceptable si se programara la solución recursiva de manera directa. La aplicación de la técnica de programación dinámica evita la repetición de cálculos mediante la memorización de la solución de cada subproblema en una tabla, de manera que no haya que calcularlo más de una vez.

Fases para la aplicación de la técnica La aplicación de la técnica de programación dinámica tiene dos fases fundamentales: 1 Definir recursivamente la solución del problema. 2 Definir la estructura de datos para memorizar las soluciones de los subproblemas y escribir el algoritmo que va calculando los valores de esa estructura de datos siguiendo la caracterización de la solución definida en la fase 1, pero sin repetir el cálculo de soluciones de subproblemas.

Combinatoria Cuántos subconjuntos de n elementos pueden formarse con un conjunto de m elementos? (n m) Denominemos S(m, n) a ese número: Si n = 0 entonces sólo puede formarse el conjunto vacío: S(m, 0) = 1 Si n > 0, el número de subconjuntos queda caracterizado por el siguiente razonamiento: Tomemos un elemento x del conjunto de m elementos. Por un lado están todos los subconjuntos que contienen a x y, por otro lado, todos los que no. En el primer caso, el número de subconjuntos es S(m 1, n 1) ya que x es uno de los elementos elegidos y los otros n 1 han de ser elegidos de entre m 1 elementos. En el segundo caso, hay que elegir n elementos de la colección de m 1 restantes (al quitar x), así que su número debe ser S(m 1, n). Por lo tanto, S(m, n) = S(m 1, n 1) + S(m 1, n) cuando n > 0 Es evidente que la programación recursiva directa de esta recurrencia produce multitud de cálculos repetidos.

Combinaciones(m, n) Definimos una estructura de datos adecuada para memorizar las soluciones de los subproblemas: Una tabla T (0..m, 0..n) tal que T (i, j) guardará S(i, j). Un algoritmo que calcula esos valores, según dicta la recurrencia definida para S(m, n), desde los casos básicos a los generales, es el siguiente: func Combinaciones(m, n) return natural for i in 0... m loop T (i, 0) 1 for j in 1... mín(i 1, n) loop T (i, j) T (i 1, j 1) + T (i 1, j) end loop if i n then T (i, i) 1 end loop return T (m, n) Análisis: Obviamente, Θ(mn) en tiempo y en espacio extra.

Funciones con memoria (1/2) Podemos escribir un algoritmo recursivo, que siga el dictado de la recurrencia para S(m, n), evitando la repetición de cálculos. Basta con poner una condición antes de realizar cada llamada recursiva (la condición de que ese valor no haya sido calculado previamente). Definimos la misma tabla T (0..m, 0..n) tal que T (i, j) guardará S(i, j). Inicializamos la tabla con valores que representen que las casillas no han sido todavía calculadas. Por ejemplo: T (i, j) 0 i : 0... m, j : 0... n

Funciones con memoria (2/2) func CombinacionesFM(m, n) return natural if T (m, n) 0 then return T (m, n) if n = 0 n = m then T (m, n) 1 return T (m, n) {n 0 n m T (m, n) no calculado } T (m, n) CombinacionesFM(m 1, n 1) + CombinacionesFM(m 1, n) return T (m, n) Análisis: Básicamente, se rellena la misma tabla de valores que con Combinaciones(m, n) (algunos valores menos) y cada casilla se rellena con un coste de O(1); así que Θ(mn) en tiempo y en espacio extra.

Programación dinámica y optimización Un requisito para el uso de la programación dinámica en la resolución de problemas de optimización es que el problema en cuestión satisfaga el Principio de optimalidad: En toda secuencia óptima de decisiones para resolver el problema, cualquier subsecuencia es óptima para resolver el subproblema correspondiente.

Programación dinámica y optimización: Fases (1/2) El diseño de un algoritmo de optimización según la técnica de programación dinámica se realiza en cuatro fases: 1 Caracterizar la estructura de una solución optimal (asegurar que se satisface el principio de optimalidad). 2 Definir recursivamente la función de optimización. 3 Escribir el algoritmo que calcula la función de optimización sin repetir cálculos de soluciones de subproblemas. 4 Escribir el algoritmo que construye una solución optimal del problema, a partir de la información calculada en el paso anterior. La fase 4 no se realiza cuando sólo se busca el valor de la función de optimización. Si hay que realizar la fase 4, en la fase 3 debe considerarse el cálculo de información adicional.

Programación dinámica y optimización: Fases (2/2) En general, las soluciones a problemas de optimización obtenidas con la técnica de programación dinámica pueden imaginarse como compuestas de una serie de opciones tomadas en estados sucesivos. Para abordar las fases 1 y 2, mencionadas antes, puede ayudar la táctica de suponer que se ha tomado una de esas opciones para llegar a la solución y analizar qué subproblemas quedarían todavía pendientes para ser resueltos y cómo se combinarían para solucionar el original de manera óptima. Obsérvese que, seguramente, esa opción inicial no es única y que interesará estudiar las demás posibilidades, quedándonos con la mejor.

Problema de la mochila con valores enteros Disponemos de n objetos de valores v i (i : 1... n) y pesos p i (i : 1... n), respectivamente. Queremos cargar una mochila con una colección de esos objetos que maximice la suma de los valores transportados. La capacidad máxima de la mochila es M 0.

Fase 1: Estructura de una solución optimal Este problema satisface el principio de optimalidad: Una carga optimal de la mochila contiene una colección de objetos S. Si retiramos un objeto de esa colección optimal S (supongamos que está numerado con el índice k), la colección restante S {k} es una forma óptima de cargar una mochila de capacidad M p k. Podemos construir una solución optimal a partir de soluciones optimales de los n subproblemas siguientes (uno por cada objeto i : 1... n): Carga óptima de una mochila de capacidad M p i considerando la colección de objetos inicial sin el objeto número i. Una solución optimal del problema original es la que más valor acumule tras sumar v i (i : 1... n) al valor de la correspondiente solución del subproblema i.

Fase 2: Definición recursiva de la función de optimización Primer intento B(C) = Máximo beneficio que puede llevarse en la mochila de capacidad C (contando con los objetos dados) B(C) = max{v i + B(C p i ) (1 i n) p i C} Inconveniente: En B(C p i ) no se refleja que el objeto i no puede tomar parte en la solución.

Fase 2: Definición recursiva de la función de optimización Segundo intento Establecemos un orden entre los elementos (según su numeración {1,..., n}). Añadimos un parámetro a la función, que representa una etapa en la resolución del problema. B(C, k) = Máximo beneficio que puede llevarse en la mochila de capacidad C cuando puedo poner objetos del conjunto {1,..., k}. k = 0 significa que el conjunto de elementos es vacío. Nos interesa el valor B(C, n) B(0, k) = 0 k B(C, 0) = 0 C B(C, k) = max{b(c, k 1), v k + B(C p k, k 1)} si k > 0 p k C B(C, k) = B(C, k 1) si k > 0 p k > C

Fase 3: Algoritmo que calcula la función de optimización Definimos una tabla T (0..M, 0..n) tal que cada casilla T (i, j) guardará el valor de B(i, j). proc Mochila (T (0..M, 0..n), p(1..n), v(1..n)) for k in 0..n loop T (0, k) 0 end loop for C in 0..M loop T (C, 0) 0 end loop for k in 1..n loop for C in 1..M loop if p(k) C then if T (C, k 1) < v(k) + T (C p(k), k 1) then T (C, k) v(k)+t(c-p(k), k-1) else T (C, k) T(C, k-1) end if else T (C, k) T(C, k-1) Análisis: Θ(Mn) en tiempo y en espacio extra

Fase 4: Construcción de una solución optimal (1/2) Añadimos unas marcas en el algoritmo, que nos indicarán la opción que se tomó en tal estado. Típicamente, la estructura para las marcas es una estructura paralela a la que guarda los resultados. objetos(c, k) = true T (C, k 1) < v k + T (C p k, k 1) Significa que en el estado (C, k) interesa tomar el objeto k

Fase 4: Construcción de una solución optimal (2/2) proc Mochila (T (0..M, 0..n), p(1..n), v(1..n)) for k in 0..n loop T (0, k) 0 end loop for C in 0..M loop T (C, 0) 0 end loop for k in 1..n loop for C in 1..M loop if p(k) C then if T (C, k 1) < v(k) + T (C p(k), k 1) then T (C, k) v(k)+t(c-p(k), k-1) objetos(c, k) = true else T (C, k) T(C, k-1) objetos(c, k) = false end if else T (C, k) T(C, k-1) objetos(c, k) = false

Nos interesa el valor de Cons Mochila(M, n). Análisis: Θ(n) Construcción de solución óptima La solución está caracterizada por la siguiente función: Cons Mochila(C, 0) = {} Cons Mochila(0, k) = {} Cons Mochila(C, k) = Cons Mochila(C, k 1) si objetos(c, k) = false Cons Mochila(C, k) = {k} Cons Mochila(C p k, k 1) si objetos(c, k) = true func Cons Mochila(C, k) return conjunto if k = 0 C = 0 then return { } else if objetos(c, k) = false then return Cons Mochila(C, k 1) else return {k} Cons Mochila(C p k, k 1)

Problema de la subsecuencia común más larga Una subsecuencia de una secuencia S se obtiene retirando de S cualquier número de elementos de cualesquiera posiciones. Por ejemplo: sedante es una subsecuencia de estupendamente. El problema que queremos resolver es el de calcular una subsecuencia común, lo más larga posible, de dos secuencias dadas R y S. Por ejemplo: Si R = aturdido y S = tranquilo, la solución es trio Si R = si y S = no, la solución es ε (secuencia vacía)

Fase 1: Estructura de una solución optimal La siguiente función recursiva f (R, S) define (por casos) una subsecuencia común de longitud maximal de dos secuencias R y S: f (R, ε) = ε f (ε, S) = ε f (a R, a S) = a f (R, S) f (a R, b S) = la de mayor longitud entre f (R, b S) y f (a R, S) si a b

Fase 2: Definición recursiva de la función de optimización Dadas las secuencias R(1..m) y S(1..n) L(i, j) = longitud de la subsecuencia más larga de R(i..m) y S(j..n) L(m + 1, j) = 0 j L(i, n + 1) = 0 i L(i, j) = 1 + L(i + 1, j + 1) si R(i) = S(j) i m j n L(i, j) = max{l(i + 1, j), L(i, j + 1)} si R(i) S(j) i m j n

Fase 3: Algoritmo que calcula la función de optimización Utilizaremos una tabla de dimensiones (m + 1) (n + 1) en la que cada posición (i, j) almacenará la solución del subproblema L(i, j) proc SubsecMasLarga (R(1..m), S(1..n), L(1..m+1, 1..n+1)) for i 1... m+1 loop L(i, n+1) 0 end loop for j 1... n+1 loop L(m+1, j) 0 end loop for i m downto 1 loop for j n downto 1 loop if R(i)=S(j) then L(i, j) 1+L(i+1, j+1) else L(i, j) max { L(i+1, j), L(i, j+1) } end loop end loop Análisis: Θ(mn) en tiempo y en uso de espacio extra.

Fase 4: Construcción de una solución optimal Por último, para calcular la subsecuencia común, que corresponde a la longitud calculada por SubsecMasLarga(R(1..m), S(1..n), L(1..m+1, 1..n+1)), completaremos ese algoritmo para dejar las marcas siguientes: X si R(i) = S(j) Marca(i, j) = 1 si R(i) S(j) L(i + 1, j) > L(i, j + 1) 2 si R(i) S(j) L(i + 1, j) < L(i, j + 1) significando que la subsecuencia de longitud L(i, j) es: R(i) Subsec(i + 1, j + 1) si Marca(i, j) = X Subsec(i, j) = Subsec(i + 1, j) si Marca(i, j) = 1 Subsec(i, j + 1) si Marca(i, j) = 2 Ahora sólo queda implementar el algoritmo que calcula Subsec(i, j) e invocarlo con los parámetros i = 1 y j = 1. Es fácil conseguir una implementación de coste O(m + n) = O(max(m, n)) en tiempo.

Problema de todos los caminos mínimos Considérese un grafo dirigido con pesos no negativos asociados a las aristas. Cual es la distancia mínima entre cada par de nodos?

Fase 1: Estructura de una solución optimal Para cada par de nodos a, b, un camino mínimo está compuesto de una sucesión de aristas. La longitud del camino puede ser 1 (la arista que va del nodo origen a al nodo destino b) o mayor que 1. En este segundo caso, habrá que pasar por algún nodo intermedio k. Evidentemente, el trozo de camino que va del origen a hasta k debe ser un camino mínimo, y lo mismo para el trozo de camino que va de k al destino b.

Fase 2: Definición recursiva de la función de optimización Asumimos que los nodos están numerados 1... n. Disponemos de la matriz de adyacencia del grafo P(1..n, 1..n): P(i, j) 0 si i j P(i, i) = 0 i P(i, i) = si no existe la arista (i, j) Primer intento: D(i, j) = Distancia mínima del nodo i al nodo j D(i, j) = min{p(i, j), D(i, k) + D(k, j) 1 k n k i k j} D(i, j) está mal definida: en su definición aparece D(i, k), que a su vez necesitaría D(i, j)

Segundo intento Añadimos un parámetro más a la función, que representa una noción de etapas en las que subdividimos el problema. D(i, j, k) = Distancia mínima de i a j cuando podemos pasar por los nodos {1... k}. Si k = 0 significa que no podemos pasar por ningún nodo intermedio. Cuando podemos pasar por los nodos {1..k}, hay dos opciones: No pasar por k, y por lo tanto utilizar sólo los restantes {1..k 1} nodos. Pasar por k, y por lo tanto realizar el camino de i a k de forma mínima usando sólo los restantes {1..k 1} nodos, y análogamente de k a j. D(i, j, 0) = P(i, j) D(i, j, k) = min{d(i, j, k 1), D(i, k, k 1) + D(k, j, k 1)}

Gestión de la estructura de memorización Necesitamos los valores D(i, j, n) i, j Podríamos calcular los valores de n matrices D(i, j, p) para p = 1..n, y quedarnos con los resultados de la n-ésima. Hay que observar que, en la etapa k, se satisface que D(i, k, k) = D(i, k, k 1) D(k, j, k) = D(k, j, k 1) La fila y columna k no se modifica en la etapa k. Por lo tanto nos basta una sola matriz D(i, j) que se va actualizando en cada etapa según la recurrencia definida.

Fase 3: Algoritmo que calcula la función de optimización func Floyd (P(1..n, 1..n)) return matriz D(1..n, 1..n) P(1..n, 1..n) for k in 1..n loop for i in 1..n loop for j in 1..n loop D(i, j) min(d(i, j), D(i, k)+d(k, j)) return D(1..n, 1..n) Análisis: Θ(n 3 ) en tiempo y Θ(n 2 ) en espacio extra

Fase 4: Construcción de una solución optimal Para poder construir los caminos mínimos entre cada par de nodos, añadimos una matriz de marcas: M(i, j) = k, significará que para ir de i a j por un camino mínimo es preciso pasar por el nodo k. Como siempre, las marcas se añaden en la parte del código donde se toman decisiones. func Floyd (P(1..n, 1..n)) return matrices D(1..n, 1..n) P(1..n, 1..n) for k in 1..n loop for i in 1..n loop for j in 1..n loop if D(i, k)+d(k, j) < D(i, j) then D(i, j) D(i, k)+d(k, j) M(i, j) k return D(1..n, 1..n) y M(1..n, 1..n)

Construcción de una solución óptima Algoritmo que construye la lista de nodos intermedios para ir del nodo i al nodo j por camino mínimo. M(1..n, 1..n) ha sido construida por el algoritmo Floyd. func Camino Floyd (M, i, j) return lista de nodos if M(i, j) = 0 then return [ ] {secuencia vacía} else k M(i, j) return Camino Floyd (M, i, k) [k] Camino Floyd (M, k, j) ( simboliza la operación de concatenación de listas.) Análisis: O(n), siendo n el número de nodos del grafo. Obsérvese que es un algoritmo lineal en la longitud del camino mínimo de i a j calculado por Floyd.