Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Diseño II:
Backtracking
Programación dinámica
Algoritmos greedy
M. Andrea Rodrı́guez-Tastets
Ayudante: Erick Elejalde
Universidad de Concepción,Chile
www.inf.udec.cl\ ∼andrea
andrea@udec.cl
I Semestre - 2015
Backtracking Programación Dinámica Algoritmos Greedy
Introducción
Backtracking
Backtracking es una forma sistemática de iterar sobre TODAS las posibles
configuraciones de un espacio de búsqueda. Estas configuraciones pueden ser
posibles permutaciones o subconjuntos del espacio.
Estos problemas generan cada una de las posibles configuraciones UNA VEZ.
Backtracking construye un árbol de soluciones parciales, donde cada vértice
representa una solución parcial. Existe un arco desde x a y si es que y fue
creado por un avance o extensión de x.
El proceso de construir una solución corresponde exactamente a un recorrido en
profundidad (depth-first) del árbol.
Backtracking Programación Dinámica Algoritmos Greedy
Backtracking (2)
Se puede modelar la búsqueda combinatorial del backtracking como sigue:
Una solución de la búsqueda es un vector a = (a1 , . . . , an ), donde cada
elemento ai es seleccionado de un conjunto finito y ordenado Si .
A cada paso del backtracking, se trata de extender el vector a = (a1 , . . . , ak )
agregando un elemento al final. Si es una solución, entonces se debe reportar
como tal.
Backtracking Programación Dinámica Algoritmos Greedy
Backtracking (3)
Algoritmo
Backtracking-DFS(A,k)
begin
if A = (a1 , . . . , ak ) es una solución then
return A
else
k =k +1
calcule (construya) Sk
while Sk 6= 0 do
ak = un elemento en Sk
Sk = Sk − ak
Backtracking−DFS(A,k)
endwhile
endif
Backtracking Programación Dinámica Algoritmos Greedy
Backtracking: Ejemplo
Considere el problema de encontrar todos los subconjunto de n elementos de un
conjunto de m elementos. Los elemento se almacenan en una arreglo S[1 . . . n].
Un candidato es una solución parcial que contiene a lo más n candidatos. El
candidato se representa por un vector binario B[1 . . . k], donde B[i] = 1 si el
elemento S[i] está en el conjunto, sino B[i] = 0.
Se puede entonces extender la solución agregando un elemento al vector B con
valor 0 o 1.
Una candidato es una solución si ki=1 B[i] = n y k = m.
P
Backtracking Programación Dinámica Algoritmos Greedy
Backtracking: comentarios
Backtracking asegura correctitud al enumerar todas las posibilidades.
Sin embargo, puede ser muy costoso construir todas las posibilidades
(permutaciones) primero y luego analizarlas. Asuma que se parte por un nodo
n1 del árbol de soluciones y que no existe un arco entre nodo n1 y n2 . Entonces
las próximas (n − 2)! permutaciones que siguen de (n1 , n2 ) no son soluciones.
Otra forma de evitar una búsqueda exhaustica es explotar simetrı́a donde
soluciones parciales son idénticas para distintos caminos y evitar la
computación repetitiva de soluciones parciales (dynamic programming).
Otra técnica es pruning, la cual que corta ciertos caminos de búsqueda en el
árbol de solución (esto es usado en greedy).
Backtracking Programación Dinámica Algoritmos Greedy
Programación dinámica
Introducción
La programación dinámica, al igual que la estrategia de Dividir para Vencer,
resuelve problemas por la combinación de soluciones a subproblemas.
Sin embargo, a diferencia de Dividir para Vencer, la programación dinámica es
aplicable cuando los subproblemas comparten subsubproblemas.
La programación dinámica resuelve cada subsubproblema solo una vez y salva
la respuesta en una tabla, evitando el trabajo de recalcular la respuesta cada
vez que el subsubproblema se encuentra.
La programación dinámica es usualmente aplicada a problemas de
optimización, donde se necesitan hacer ciertas selecciones para llegar a una
solución óptima, y donde al hacer una selección, subproblemas de la misma
forma deben resolverse.
Backtracking Programación Dinámica Algoritmos Greedy
Introducción (cont.)
El desarrollo de un algoritmo que use la técnica de programación dinámica puede ser
desglosado en los siguientes pasos
Caracterización de la estructura de una solución óptima.
Definir recursivamente el valor de una solución óptima.
Calcular el valor de una solución óptima en un esquema bottom-up
Construir una solución óptima a partir de la información calculada. Este último
paso puede ser omitido si solo el valor de una solución óptima es requerido.
Backtracking Programación Dinámica Algoritmos Greedy
t1,n.1 x1
e1 t1,1 t1,2 t1,3
Salida
t2,n-1 autos
Entrada t2,1 t2,2 t2,3 x2
ensamblaje e2
Hay 2n formas diferentes de escoger estaciones en una lı́nea de ensamblaje, lo que es imposible para un número
muy grande de n.
Backtracking Programación Dinámica Algoritmos Greedy
En este caso, la forma más rápida de pasar por S1,j es una de las dos siguientes
opciones:
La forma más rápida de pasar por S1,j−1 y entonces directamente por S1,j .
La forma más rápida de pasar por S2,j−1 y entonces transferirse a lı́nea 1 y
pasar por S1,j .
Backtracking Programación Dinámica Algoritmos Greedy
Se puede hacer mucho mejor considerando que para j ≥ 2, cada valor fi [j] depende
solo de los valores de f1 [j − 1] y f2 [j − 1]. Calculando fi [j] en orden creciente en el
número de la estación (izquierda a derecha), se puede determinar la forma más rápida
de pasar por la fábrica, y eso toma Θ(n).
Backtracking Programación Dinámica Algoritmos Greedy
Ensamblaje(a, t, e, x, n)
begin
f1 [1] ← e1 + a1,1
f2 [1] ← e2 + a2,1
for j ← 2 to n do
if f1 [j − 1] + a1,j ≤ f2 [j − 1] + t2,j−1 + a1,j then
f1 [j] ← f1 [j − 1] + a1,j
l1 [j] ← 1
else f1 [j] ← f2 [j − 1] + t2,j−1 + a1,j
l1 [j] ← 2
endif
if f2 [j − 1] + a2,j ≤ f1 [j − 1] + t1,j−1 + a2,j then
f2 [j] ← f2 [j − 1] + a2,j
l2 [j] ← 2
else f2 [j] ← f1 [j − 1] + t1,j−1 + a2,j
l2 [j] ← 1
endif
endfor
if f1 [n] + x1 ≤ f2 [n] + x2 then
f ∗ ← f1 [n] + x1
l∗ ← 1
else f ∗ ← f2 [n] + x2
l∗ ← 2
endif
end
Backtracking Programación Dinámica Algoritmos Greedy
línea 1 4 8
7 9 3 4
1 4 3
2 2 3 1
Salida
1 autos
Entrada 2 1 2 2 2
ensamblaje
4
8 5 6 4 5 7
línea 2
2 3 4 5 6
j 1 2 3 4 5 6
f1[j] 9 18 20 24 32 35 l1[j] 1 2 1 1 2
f2[j] 12 16 22 25 30 37 l2[j] 1 2 1 2 2
f* = 38 l* = 1
Backtracking Programación Dinámica Algoritmos Greedy
Imprime
begin
i ← l∗
printf “lı́nea ”i, “estación ”n
for j ← n downto 2 do
i ← li [j]
printf “lı́nea ”i, “estación ”j − 1
enddo
end
Backtracking Programación Dinámica Algoritmos Greedy
Multiplicación de matrices
Asuma una secuencia (cadena) de n matrices (A1 , A2 , . . . , An ) para ser multiplicadas:
A1 A2 . . . An . Este problema se puede resolver usando las funciones de multiplicación
entre dos matrices cuando las matrices han sido organizadas con paréntesis. La
multiplicación en asociativa de manera que todas las formas de colocar paréntesis
deberı́an llevar a un mismo producto.
La forma en que se colocan los paréntesis puede tener un fuerte impacto en el costo
en que se evalúa el producto. Por ejemplo, asuma una cadena (A1 , A2 , A3 ) de tres
matrices. Suponga además que las matrices son de dimensiones
10 × 100, 100 × 5, 5 × 50, respectivamente. Entonces, si agrupamos de la siguiente
forma ((A1 A2 )A3 ) se realizan 10 × 100 × 5 = 5000 multiplicaciones escalares para
multiplicar A1 A2 que resulta en una matriz de 10 × 5, más 10 × 5 × 50 = 2500
multiplicaciones para obtener la multiplicación de la matriz resultante con A3 , sumando
un total de 7500 multiplicaciones. Si agrupamos (A1 (A2 A3 )), sin embargo, se realizan
75000 multiplicaciones.
Backtracking Programación Dinámica Algoritmos Greedy
Uno puede obtener una recurrencia sobre el número de alternativas para colocar los
paréntesis definida de la siguiente forma, donde P(n) es el número de formas
alternativas de colocar paréntesis en una cadena de n matrices, k identifica la
k−ésima y (k + 1)−ésima matrices entre las cuales se divide el problema es dos
subcadenas con paréntesis:
1 if n = 1
P(n) = Pn−1
k =1
P(k)P(n − k) if n ≥ 2
Esta recurrencia es acotada por Ω(4n /n3/2 ). Por lo tanto, una forma exhaustiva de
buscar las formas de evaluar una cadena de matrices no es un eficiente algoritmo.
Backtracking Programación Dinámica Algoritmos Greedy
En consecuencia se puede construir una solución óptima para una instancia del
problema de mutiplicación de una cadena de matrices al dividir el problema en dos
subproblemas Ai Ai+1 . . . Ak y Ak+1 Ak+2 . . . Aj , para los cuales se pueden encontrar
soluciones óptimas y finalmente combinar estas soluciones.
Backtracking Programación Dinámica Algoritmos Greedy
Ejercicio
Encuentre los paréntesis para solucionar la cadena de multiplicación de matrices cuya
secuencia de dimensiones es (5, 10, 3, 12, 5, 50, 6).
Backtracking Programación Dinámica Algoritmos Greedy
Subestructuras óptimas
El primer paso de usar programación dinámica para resolver un problema es
caracterizar la estructura de la solución óptima. Esto es, un problema tiene una
subestructura óptima si es que la solución óptima del problema contiene en ella
soluciones óptimas a los subproblemas. Consecuentemente, se debe asegurar que el
rango de subproblemas incluye aquellos usados en la solución óptima.
Backtracking Programación Dinámica Algoritmos Greedy
q r
s t
Memoization
Un alternativa a la programación dinámica, que mantiene su rendimiento pero que usa
una estrategia top-down, es la de memorizar los resultados de un algoritmo recursivo
pero ineficiente. Un algoritmo recursivo en este enfoque mantiene una entrada en una
tabla por cada solución a un subproblema. Cada entrada contiene inicialmente un
valor que indica que la entrada debe aún ser llenada.
Backtracking Programación Dinámica Algoritmos Greedy
revisarCadena(p, i, j)
begin
if m[i, j] < ∞ then
return m[i, j]
endif
if i = j then
m[i, j] ← 0
else for k ← i to j − 1 do
q ← revisarCadena(p, i, k ) + revisarCadena(p, k + 1, j) + pi−1 pk pj
if q < m[i, j] then
m[i, j] ← q
endif
endfor
endif
end
El algoritmo corre en O(n3 ) al igual que la solución bottom-up.
Backtracking Programación Dinámica Algoritmos Greedy
k2
k5
k2 k1
d5
k1 k4
k4
d0 d1
d0 d1 k3
k3 k5 d4
d2 d3 d4 d5 d2 d3
i 0 1 2 3 4 5
pi 0.15 0.1 0.05 0.1 0.2
qi 0.05 0.1 0.05 0.05 0.05 0.1
Backtracking Programación Dinámica Algoritmos Greedy
n
X n
X
E[T ] = (depthT (ki ) + 1)pi + (depthT (di ) + 1)qi
i=1 i=0
n
X n
X
= 1+ depthT (ki )pi + depthT (di )qi
i=1 i=0
j
X j
X
w(i, j) = pl + ql
l=i l=i−1
Notar que
optimalBST (p, q, n)
begin
n ← length[p] − 1
for i = 1 to n + 1 do
e[i, i − 1] ← qi−1
w[i, i − 1] ← qi−1
endfor
for l = 1 to n do
for i = 1 to n − l + 1 do
j ←i +l −1
e[i, j] ← ∞
w[i, j] ← w[i, j − 1] + pj + qj
for r = i to j do
t ← e[i, r − 1] + e[r + 1, j] + w[i, j]
if t < e[i, j] then
e[i, j] ← t
else
root[i, j] ← r
endif
endfor
endfor
endfor
end
Backtracking Programación Dinámica Algoritmos Greedy
node probabilidad
k1 0.15
k2 0.1
k3 0.05
k4 0.1
k5 0.2
d0 0.05
d1 0.1
d2 0.05
d3 0.05
d4 0.05
d5 0.1
Backtracking Programación Dinámica Algoritmos Greedy
Ejemplo
Sea el conjunto S = {1, 2, 3, 4, 5, 6, 7, 8, 9}.
m k d k
n 1 2 3 n 1 2 3
1 1 1 1 1 - - -
2 3 2 2 2 - 1 1
3 6 3 3 3 - 2 2
4 10 6 4 4 - 3 3
5 15 9 6 5 - 3 4
6 21 11 9 6 - 4 5
7 28 15 11 7 - 5 6
8 36 21 15 8 - 5 6
9 45 24 17 9 - 6 7
Backtracking Programación Dinámica Algoritmos Greedy
Ejercicio
Ema y Camilo disfrutan de pasear por las montañas, sin embargo, la cordillera tiene
tantos cerros distintos que generalmente para moverse de un punto a otro deben subir
y bajar repetidamente. Ellos desean encontrar un camino que los lleve de un punto a
otro tal que, o bien sólo suba, o bien sólo baje o bien se mantengan por ciertos tramos
a la misma altura.
Ema y Camilo tienen un mapa que indica la altura del terreno en cada punto. Estos
puntos forman una grilla equiespaciada en el terreno. Para evitar perderse Ema y
Camilo quisieran que les entregaras instrucciones muy simples. Debes indicarles
cómo moverse desde el punto de partida hasta el final. Los posibles movimientos son
(N)orte, (S)ur, (E)ste y (O)este. Obviamente, Ema y Camilo quieren recorrer lo menos
posible, por lo que tu programa deberá ser capaz de encontrar la ruta con la menor
cantidad de movimientos.
Backtracking Programación Dinámica Algoritmos Greedy
Algoritmos Greedy
Introducción
Los algoritmos de optimización usualmente pasan por un secuencia de pasos,
con un conjunto de elecciones a realizar en cada paso.
Para algunos casos la programación dinámica en buscar una solución óptima
puede ser muy costosa.
Un algoritmo greedy siempre escoge una alternativa que parece mejor en el
momento de la elección. Esto logra una elección óptima local.
Algoritmo greedy no siempre llevan a una solución óptima. En este sesión
veremos problemas para los cuales algoritmos greedy encuentran soluciones
óptimas.
Backtracking Programación Dinámica Algoritmos Greedy
Si denotamos c[i, j] el tamaño de la solución para el conjunto Sij , este valor a optimizar
se define como:
0 If Sij = ∅
c[i, j] = max {c[i, k] + c[k, j] + 1} If S 6= ∅
ak ∈Si,j i,j
Backtracking Programación Dinámica Algoritmos Greedy
Subestructura óptima
El problema exhibe una subestructura óptima si una solución óptima para el problema
contiene soluciones óptimas a subproblemas. Esto es clave tanto para programación
dinámica como para algoritmos greedy.
En el caso de algoritmos greedy, lo que se necesita hacer es mostrar que una solución
óptima a un subproblema, combinado con la elección hecha en ese paso, lleva a una
solución óptima del problema original. Este esquema usa intuitivamente inducción en
los subproblemas para probar que hacer la elección greedy en cada paso produce una
solución óptima.
Backtracking Programación Dinámica Algoritmos Greedy
20
30
30
30
20 20
30
20 20
10 10 10 10
Código Huffman
La codificación de Huffman es una codificación de prefijo de
largo variable para la compresión de cadena de caracteres.
A diferencia de una codificación de largo fijo (como el ASCII),
largo variable permite optimizar y comprimir en más de un 20 %
la cadena de caracteres
Códigos son asignados a caracteres tal que el largo del código
depende de la frecuencia relativa del caracter.
a b c d e f
Frecuencia 45 13 12 16 9 5
Código de largo fijo 000 001 010 011 100 101
Código de largo variable 0 101 100 111 1101 1100
Backtracking Programación Dinámica Algoritmos Greedy
Árboles de presentación
caracter c d e f k l u z
frecuencia 32 42 120 24 7 42 37 2
a
Ejemplo extraı́do desde Huffman Coding por Lawrence Brown, 1999
Backtracking Programación Dinámica Algoritmos Greedy
Character C D E F K L U Z
Frequency 32 42 120 24 7 42 37 2
• Construcci
The first step
ón:is paso
to construct
1 a Priority Queue and insert each
frequency-character (key-element) pair into the queue.
El primer paso construye una cola de prioridad por frecuencia de
caracteres
• Step 1:
2 7 24 32 37 42 42 120
Z K F C U L D E
25 September, 1999
7
Huffman Coding
Backtracking
Lawrence M. Brown
Programación Dinámica Algoritmos Greedy
• A new
Construcci ón:Binary
pasoTree
2 is created with the lowest-key Item as the left
external node, and the second lowest-key Item as the right external
En el segundo
node. paso, los dos elementos con menor frecuencia son
removidos
• Thede la Tree
new colaisde prioridad.
then Se crea
inserted back un priority
into the árbol binario
queue. con el
elemento de menor frecuencia en la izquierda y el de mayor
frecuencia
• Step 2:en la derecha. El árbol se insertar al final de la cola
24 32 37 42 42 120
9 F C U L D E
2 7
Z K
25 September, 1999
8
Backtracking Programación Dinámica Algoritmos Greedy
uffman Coding
wrence M. Brown
Step 3: 32
C
33
37
U
42
L
42
D
120
E
9 24
F
2 7
Z K
Step 4: 37 42 42 120
U L D 65 E
32
33
C
Backtracking Programación Dinámica Algoritmos Greedy
9 24
F
2 7
Z K
Construcción: paso 4
tep 4: 37 42 42 120
U L D 65 E
32
33
C
9 24
F
2 7
Z K
ber, 1999
Backtracking Programación Dinámica Algoritmos Greedy
Huffman Coding
Lawrence M. Brown
42 120
D 65 79 E
32 37 42
33
C U L
9 24
F
2 7
Z K
25 September, 1999
10
Backtracking Programación Dinámica Algoritmos Greedy
o decode a bit stream (from the leftmost bit), start at the root node of the Tree:
• move to the left child if the bit is a “0”.
• move to the right child if the bit is a “1”.
• WhenConstrucci
an external node is reached,
ón: paso finalthe character at the node is sent to the
decoded string.
Hasta
• The next llegar
bit is al final, from
then decoded lo quetheocurre en tree.
root of the este caso en el paso n = 8
306
0 1
120 186
E
0 1
Decode:
79 107
1011001110111101 0 1
0 1
L1001110111101
37 42 42 65
L U1110111101 U L D
0 1
L U C111101
L U C K 32
33
C
0 1
9 24
F
0 1
2 7
Z K
mber, 1999
13
Backtracking Programación Dinámica Algoritmos Greedy
Huffman Coding
Lawrence M. Brown
25 September, 1999
12
En este algoritmo Q es implementada como un min − heap. El tiempo tiempo entonces de este algoritmo es de
nlogn ya que se realizan n − 1 operaciones de extracción de valores de la fila (costo O(logn)).
Backtracking Programación Dinámica Algoritmos Greedy
0
B(T ) − B(T ) =
X X
= c.freq × dT (c) − c.freq × dT 0 (c)
c∈C c∈C
= x.freq × dT (x) + a.freq × dT (a) − x.freq × dT 0 (x) − a.freq × dT 0 (a)
= x.freq × dT (x) + a.freq × dT (a) − x.freq × dT (a) − a.freq × dT (x)
= (a.freq − x.freq)(dT (a) − dT (x))
≥ 0
con dT (c) la profundidad de c en árbol T . Tanto a.freq − x.freq y dT (a) − dT (x) son
no negativos, teniendo entonces que B(T 0 ) ≤ B(T ). Ası́ podemos entonces hacer el
intercambio de b por y logrando un árbol T 00 tal que B(T 00 ) ≤ B(T ). Ya que T es
óptimo entonces B(T 0 ) ≤ B(T 00 ) y por lo tanto B(T 0 ) = B(T 00 )
Con este lema entonces podemos decir que una solución óptima puede comenzar por
escoger los dos frecuencias más bajas. Ahora se necesita ver que exista una
subestructura óptima.
Backtracking Programación Dinámica Algoritmos Greedy
Algoritmo: subestructura
Lema: Sea C un alfabeto en el cual el caracter c ∈ C tiene frecuencia c.freq. Sea x e
y dos caracteres en C teniendo la frecuencia menor. Sea z parte de un alfabeto C 0 tal
que se han removido x e y de C y se reemplazan por z con frecuencia
z.freq = x.freq + y.freq. Sea T 0 un árbol óptimo para C 0 . Entonces el árbol T donde
el node hoja z se reemplaza por un node interno con hijos x e y representa un código
de prefijo óptimo para C.
Backtracking Programación Dinámica Algoritmos Greedy
u'
v'
v ei u
Ti-1
Si ahora borramos el otro arco (v 0 , u 0 ) del ciclo, se obtiene otro árbol de cobertura
mı́nimo del grafo completo cuyo peso total es menor que el peso de T , ya que el peso
de ei es menor o igual al de (v 0 , u 0 ). Hemos llegado a una contradicción.
Backtracking Programación Dinámica Algoritmos Greedy
m[3] <= 2 (Si m[3] > 2, entonces reemplace los 1’s por un 3
No se puede tener que m[3] > 0 y m[2] > 0, de lo contrario se usarı́an 4 para
reduce el número de monedas.
m[2] < 4, de lo contrario use 4 para reducir el número de monedas.
En general, Greedy falla en obtener la solución óptima, dependiendo de los tipos
de monedas. Sin embargo, se pueden encontrar soluciones óptimas pero que no
son únicas. 9 = 3 + 3 + 3 = 4 + 4 + 1.