programación dinámica 2

37
PROGRAMACIÓN DINÁMICA 2

Upload: jose-alberto-noh-noh

Post on 06-Aug-2015

74 views

Category:

Education


2 download

TRANSCRIPT

Page 1: Programación dinámica 2

PROGRAMACIÓN DINÁMICA 2

Page 2: Programación dinámica 2

Introducción §  Programación dinámica es una técnica de algoritmo.

§  Fue inventado por Richard Bellman en 1950.

§  No tiene nada que ver con la programación

§  Divide el problema en sub-problemas (como Divide y

Vencerás)

§  Retiene en memoria las soluciones de los sub-

problemas, para evitar cálculos repetidos (memoizing)

§  Devuelve la solución óptima (principio de Optimalidad de

Bellman)

Page 3: Programación dinámica 2

Introducción

La solución de problemas mediante esta técnica se basa en el llamado principio “de óptimo” enunciado por Bellman en 1957 y que dice: “En una secuencia de decisiones óptima toda subsecuencia ha de ser también óptima”.

Page 4: Programación dinámica 2

Programación Dinámica VS Divide y Vencerás

Ambos combinan soluciones de subproblemas, pero §  DyV se aplica cuando los subproblemas son independientes §  PD se aplica cuando los subproblemas se solapan

DyV repetiría muchos cálculos. PD los mantiene en memoria §  DyV utiliza recurrencias (+ tiempo, - memoria) §  PD intenta evitar recurrencias, utiliza memoria para iteración

(- tiempo, + memoria) §  En ambos casos se obtiene la solución óptima

Page 5: Programación dinámica 2

Pasos de un algoritmo de Programación Dinámica

§  Planteamiento de la solución como una sucesión de

decisiones y verificación de que ésta cumple el

principio de Bellman.

§  Definición recursiva de la solución.

§  Cálculo del valor de la solución óptima mediante una

tabla en donde se almacenan soluciones a problemas

parciales para reutilizar los cálculos.

§  Construcción de la solución óptima haciendo uso de la

información contenida en la tabla anterior.

Page 6: Programación dinámica 2

Ejemplo 1: Cálculo de los

Números Fibonacci

Page 7: Programación dinámica 2

Antes de abordar problemas mas complejos veamos un primer

ejemplo en el cual va a quedar reflejada toda esta problemática.

Se trata del cálculo de los términos de la sucesión de números

Fibonacci. Dicha sucesión podemos expresarla recursivamente

en términos matemáticos de la siguiente manera: Fib(n)

Cálculo de los Números de Fibonacci

1 si n=0,1 Fib(n-1) + Fib(n-2) si n >1

Page 8: Programación dinámica 2

Por tanto, la forma mas 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;

Cálculo de los Números de Fibonacci

Page 9: Programación dinámica 2

El inconveniente es que el algoritmo resultante es poco

eficiente ya que su tiempo de ejecución es de orden

exponencial.

Como podemos observar, la falta de eficiencia del algoritmo

se debe a que se producen llamadas recursivas repetidas

para calcular valores de la sucesión, que habiéndose

calculado previamente, no se conserva el resultando y por

tanto es necesario volver a calcular cada vez.

Cálculo de los Números de Fibonacci

Page 10: Programación dinámica 2

Para este problema es posible diseñar un algoritmo que en tiempo

lineal lo resuelva mediante la construcción de una tabla que permita

ir almacenando los cálculos realizados hasta el momento , para

poder reutilizarlos:

Fib(0) Fib(1) Fib(2) … Fib(n)

Cálculo de los Números de Fibonacci

Page 11: Programación dinámica 2

El algoritmo iterativo que calcula la sucesión de Fibonacci utilizando tal tabla es: TYPE TABLA = ARRAY [ 0..N] OF CARDINAL PROCEDURE FibIter ( VAR T: TABLA; n : CARDINAL) : CARDINAL; VAR i : CARDINAL; BEGIN IF n<=1 THEN RETURN 1 ELSE T[0] : =1; T[1] : =1; FOR i :=2 TO n DO T[i] :=T[i-1] + T[i-2] END RETURN T[n] END END FibIter;

Cálculo de los Números de Fibonacci

Page 12: Programación dinámica 2

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:

PROCEDURE FibIter2(n: CARDINAL):CARDINAL; VAR i,suma,x,y:CARDINAL; (* x e y son los 2 últimos términos *)

BEGIN IF n<=1 THEN RETURN 1 ELSE

x:=1; y:=1; FOR i:=2 TO n DO

suma:=x+y; y:=x; x:=suma; END; RETURN suma

END END FibIter2;

Cálculo de los Números de Fibonacci

Page 13: Programación dinámica 2

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(n) pasa a ser O(1) ya que hemos eliminado la tabla. El uso de estructuras (vectores o tablas) para eliminar la repetición de los cálculos, pieza clave de los algoritmos de Programación Dinámica, hace que en este capítulo nos fijemos no sólo en la complejidad temporal de los algoritmos estudiados, sino también en su complejidad espacial. En general, los algoritmos obtenidos mediante la aplicación de esta técnica consiguen tener complejidades (espacio y tiempo) bastante razonables, pero debemos evitar que el tratar de obtener una complejidad temporal de orden polinómico conduzca a una complejidad espacial demasiado elevada

Cálculo de los Números de Fibonacci

Page 14: Programación dinámica 2

Ejemplo 2: Cálculo de los

Coeficientes Binomiales

Page 15: Programación dinámica 2

En la resolución de un problema, una vez encontrada la expresión

recursiva que define su solución, muchas veces la dificultad estriba

en la creación del vector o la tabla que ha de conservar los

resultados parciales. Así en este segundo ejemplo, aunque

también sencillo, observamos que vamos a necesitar una tabla

bidimensional algo más compleja. Se trata del cálculo de los

coeficientes binomiales, definidos como:

Cálculo de los Coeficientes Binomiales

Page 16: Programación dinámica 2

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:

Cálculo de los Coeficientes Binomiales

Page 17: Programación dinámica 2

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: T ABLA; 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.

Cálculo de los Coeficientes Binomiales

Page 18: Programación dinámica 2

Ejemplo 3: Coin Row Problem

Problema de las monedas.)

Page 19: Programación dinámica 2

Problema de las monedas

Hay una fila de n monedas cuyos valores son algunos números

enteros positivos, pero no son necesariamente distintos. El

problema se resuelve con programación dinámica, encontrando la

cantidad máxima de dinero con la restricción de que no se pueden

tomar dos monedas adyacentes.

Page 20: Programación dinámica 2

Pseudocódigo Una recurrencia de F(n) se obtiene dividiendo todas las selecciones de

monedas permitidos en dos grupos:

§  El primero es para las que incluyen la última moneda y el segundo

está compuesto por los que no.

§  La relación de recurrencia resultante es F (n) = max {c n + F (n-2), F

(n-1)} para n> 1, F (0) = 0, F (1) = c1.

§  Un bucle for se utiliza para iterar a través de la gama de valores de

la moneda mientras se utiliza el máximo para emitir la mayor

cantidad de dinero que puede ser recogido.

§  Para evitar repetir los mismos cálculos durante el backtracing, la

información acerca de cuál de los dos términos de la recurrencia era

más grande se puede guardar en una matriz cuando se calculan los

valores de F.

Page 21: Programación dinámica 2

Algoritmo del problema de las monedas (C [1..n]).

// Se aplica la fórmula (F (n) = max {c n + F (n-2), F (n-1)} // para n> 1, F (0) = 0, F (1) = c1) inferior hasta encontrar la cantidad máxima //de dinero que puede ser recogido de una fila de monedas sin recoger dos //monedas adyacentes //Entrada: Array C [1..n] de enteros positivos que indica los valores de la //moneda // Salida: El dinero máximo para f que puede ser recogido F[0] ß 0; F[1] ß C[1] For i ß 2 to n do F[i] ß max(C[i] + F[i – 2], F[i – 1]) Return F[n]

Page 22: Programación dinámica 2

Problema Fila de monedas con PD

§  La cantidad más grande que podemos obtener del primer grupo

es igual a Cn + F(n-2). §  El valor de la moneda enésima más la cantidad que podemos recoger de

los primeros n-2 monedas.

§  El importe máximo que podemos obtener del segundo grupo es

igual a F(n-1). §  Por la definición de F(n) (Uno antes como en Fibonacci).

F (n) = max { C n + F (n-2), F (n-1) } Grupo 1 Grupo 2

Page 23: Programación dinámica 2

Ejemplo:

La aplicación del algoritmo para las monedas de: 5,1,2,10,6,2 es:

Index 0 1 2 3 4 5 6 C 5 1 2 10 6 2 F 0 5

F[0] = 0, F[1] = C1 = 5

Index 0 1 2 3 4 5 6 C 5 1 2 10 6 2 F 0 5 5

F[2] = max{1 + 0, 5} = 5

Page 24: Programación dinámica 2

Ejemplo: Index 0 1 2 3 4 5 6

C 5 1 2 10 6 2 F 0 5 5 7

F[3] = max{2 + 5, 5} = 7

Index 0 1 2 3 4 5 6 C 5 1 2 10 6 2 F 0 5 5 7 15

F[4] = max{10 + 5, 7} = 15

Index 0 1 2 3 4 5 6 C 5 1 2 10 6 2 F 0 5 5 7 15 15

F[5] = max{6 + 7, 15} = 15

Page 25: Programación dinámica 2

Ejemplo:

Index 0 1 2 3 4 5 6 C 5 1 2 10 6 2 F 0 5 5 7 15 15 17

F[6] = max{2 + 15, 15} = 17

La solución del problema de monedas con programación dinámica para la fila de monedas: 5, 1, 2, 10, 6, 2. es igual a 17.

Page 26: Programación dinámica 2

Ejemplo 4: Change Making Problem (Cambio de monedas.)

Page 27: Programación dinámica 2

Change Making Problem Dar cambio por una cantidad n, usando el mínimo número de monedas disponibles. d1<d2<…dm. En este ejemplo se asume el caso general en donde: d1= 1 Y que F(n)= El número mínimo de monedas y que la suma de este sea = n; Es conveniente definir que F(0) = 0

iz

Page 28: Programación dinámica 2

Change Making Problem La cantidad de n sólo se puede obtener de la suma de la denominación de la moneda dj a la cantidad de n-dj Para j=1,2…m tal que n ≥ dj Por lo tanto podemos considerar todas las denominaciones tal que al escogerlas sea el número mínimo de monedas así que F(n-dj) + 1 Tomando en cuenta que el uno por ser constante se puede primero encontrar a F(n-dj) y luego agregar al 1. Por lo tanto se puede considerar lo siguiente F(n) = min { F (n – dj ) } + 1 para n > 0 , F(0) = 0

Page 29: Programación dinámica 2

Change Making Problem Algorithm Change Making (D[1…m], n) // Encuentra el número mínimo de monedas para dar cambio // d1<d2<…<dm donde d1= 1 y que al sumar las dj = n // Entrada: Enteros Positivos indicando los valores del arreglo [1…m] // Donde [1]= 1 y que sumando esos valores = n // Salida = número mínimo de monedas a entregar que sumadas nos den n F[0] = ← 0 For i ← 1 to n do Temp ← ∞; j ← 1 while j ≤ m and i ≥ D [j] do temp ← min (F[i – D[j]], temp) j ← j + 1 F[i] ← temp + 1 Return F [n]

Page 30: Programación dinámica 2

Change Making Problem Ejemplo n= 6 Monedas con denominaciones de: 1, 3, 4

n 0 1 2 3 4 5 6 f 0 F[0]= 0

n 0 1 2 3 4 5 6 f 0 1 F[1] = min {F[1-1]+1 = 1

n 0 1 2 3 4 5 6 f 0 1 2 F[2] = min {F[2-1]+1 = 2

n 0 1 2 3 4 5 6 f 0 1 2 1 F[3] = min {F[3-1], F[3-3]}+1 = 1

Page 31: Programación dinámica 2

Change Making Problem Ejemplo n= 6 Monedas con denominaciones de: 1, 3, 4

n 0 1 2 3 4 5 6 f 0 1 2 1 1

n 0 1 2 3 4 5 6 f 0 1 2 1 1 2

n 0 1 2 3 4 5 6 f 0 1 2 1 1 2 2

F[4] = min {F[4-1], F[4-3], F[4-4]}+1 = 1

F[5] = min {F[5-1], F[5-3], F[5-4]}+1 = 2

F[6] = min {F[6-1], F[6-3], F[6-4]}+1 = 2

Page 32: Programación dinámica 2

Ejemplo 4: Coin Collecting Problem

(Recolección de Monedas)

Page 33: Programación dinámica 2

Coin Collecting Problem

Varias monedas se colocan en las celdas de un tablero de n * m, una

sola moneda por celda.

Un robot, que se encuentra en la celda superior izquierda del tablero,

tiene que recoger la mayor cantidad de monedas como sea posible y

llevarlos a la celda inferior derecha.

En cada paso, el robot puede moverse ya sea una celda a la derecha

o una celda abajo desde su ubicación actual. Cuando el robot visita

una celda con una moneda, siempre recoge esa moneda.

Page 34: Programación dinámica 2

Coin Collecting Problem solución:

Sea F (i, j) es el más grande número de monedas que el robot puede

recoger y llevar a la celda (i, j) en la fila i y j columna del tablero. Se

puede llegar a esta celda, ya sea de la celda adyacente (i-1, j) o la

celda (i, j-1).

Recibimos la siguiente fórmula:

F (i, j) = max {F (i-1, j), F (i, j-1)} + C [i, j] for 1 <= i <= n, 1 <= j <= m

F(0,j) = 0 for 1 <= j <= m 1 y F(i,0) = 0 for 1<= i <= n,

Page 35: Programación dinámica 2

Coin Collecting Problem Algorithm // Aplica programación dinámica para contar el número más grande de //monedas // que un robot puede recolectar en un tablero de n * m, iniciando en la //posición (1,1) // Y moviéndose hacia la derecha o hacia abajo // Entrada: Matriz C[1…n, 1…m] y sus elementos son uno o cero, // Para celdas con o sin moneda respectivamente. // Salida = El máximo número de monedas que el robot puede recolectar a la //celda (n.m) F[1,1] = ← C[1,1]; for j ← 2 to m do F[1,j] ← F[1,j-1]+ C[1,j] For i ← 2 to n do

F[1,j] ← F[i-1 ,1]+ C[i,1] For j ← 2 to m do

F (i, j) ← max (F [i-1, j], F [i, j-1]) + C [i, j] Return F[n,m]

Page 36: Programación dinámica 2

Coin Collecting Ejemplo (a) monedas para recolectar (b) Resultado, utilizando programación dinámica .

(c)  camino para recolectar el máximo número de monedas

Page 37: Programación dinámica 2