Algoritmos y Estructuras de Datos II
BacktrackingClase de hoy
1 Repaso
Divide y vencerás Algoritmos voraces
2 Backtracking
Forma general de algoritmos voraces
¿Cuándo no hay un buen criterio de selección? Problema de la moneda
Problema de la mochila
Camino de costo mínimo entre todo par de vértices
Repaso
cómo vs. qué 3 partes
1 análisis de algoritmos
2 tipos de datos
3 técnicas de resolución de problemas
divide y vencerás algoritmos voraces backtracking
programación dinámica recorrida de grafos
Divide y vencerás
Ordenación por intercalación,O(nlogn). Ordenación rápida,O(nlogn)en la práctica. Búsqueda binaria,O(logn).
Exponenciación,O(logn)número de multiplicaciones (n
es el exponente).
Multiplicación de grandes números,O(nlog23)dondenes
Algoritmos voraces
Problema de la monedaO(n)dondenes el número de denominaciones si están
ordenadas.
no anda para cualquier conjunto de denominaciones
Problema de la mochila
O(n)dondenes el número de objetos si están ordenados
según sus cocientesvi/wi.
sólo anda para objetos fraccionables
Problema del árbol generador de costo mínimo
Prim esO(|V|2)dondeV es el conjunto de vértices. Se
puede mejorar con implementaciones ingeniosas.
Kruskal esO(|A|log|V|)dondeAes el conjunto de aristas.
Boruvka esO(|A|log|V|).
Problema del camino de costo mínimo
Forma general de algoritmos voraces
fungreedy(C)retS
{C: conjunto de candidatos, S: solución a construir} S:= {} doS no es solución→c:= seleccionar de C C:= C-{c} ifS∪{c} es factible→S:= S∪{c}fi od end fun
Ser solución y ser factible no tienen en cuenta optimalidad. Optimalidad depende totalmente del criterio de selección.
¿Cuándo no hay un buen criterio de selección?
A veces no hay un criterio de selección que garantice optimalidad.Por ejemplo:
Problema de la moneda para conjuntos de denominaciones arbitrarios.
Problema de la mochila para objetos no fraccionables.
En este caso, si se elige un fragmento de solución puede
ser necesario “volver hacia atrás” (backtrack) sobre esa
elección e intentar otro fragmento.
En la práctica, estamos hablando de considerar todas las selecciones posibles e intentar cada una de ellas para saber cuál de ellas conduce a la solución óptima.
Problema de la moneda
Seand1,d2, . . . ,dnlas denominaciones de las monedas
(todas mayores que 0),
no se asume que estén ordenadas,
se dispone de una cantidad infinita de monedas de cada denominación,
se desea pagar un montok de manera exacta,
utilizando elmenor número de monedas posibles.
Vimos que el algoritmo voraz puede no funcionar para ciertos conjuntos de denominaciones.
Daremos un algoritmo consistente en considerar todas las combinaciones de monedas posibles.
Simplificación y generalización
Simplificamos el problema:sólo nos interesa por ahora hallar el menor número de monedas necesario,
no nos interesa saber cuáles son esas monedas.
Generalizamos el problema:
Sean 0≤i ≤ny 0≤j ≤k,
definimosm(i,j) =“menor número de monedas necesarias
para pagar exactamente el montoj con denominaciones
d1,d2, . . . ,di.”
La solución del problema original se obtiene calculando m(n,k).
Definiendo
m
(
i
,
j
)
Casoj=0
Recordemos quem(i,j) =“menor número de monedas
necesarias para pagar exactamente el montojcon
denominacionesd1,d2, . . . ,di.”
Definiendo
m
(
i
,
j
)
Casoj>0 yi=0
Recordemos quem(i,j) =“menor número de monedas
necesarias para pagar exactamente el montojcon
denominacionesd1,d2, . . . ,di.”
m(i,0) =0,
j >0⇒m(0,j) =∞, ya que no hay manera posible de pagar el monto
Definiendo
m
(
i
,
j
)
Casoi>0 ydi>j>0
Recordemos quem(i,j) =“menor número de monedas
necesarias para pagar exactamente el montojcon
denominacionesd1,d2, . . . ,di.”
m(i,0) =0,
j >0⇒m(0,j) =∞,
i >0∧di >j>0⇒m(i,j) =m(i−1,j), ya que no se
pueden usar monedas de denominacióndi, es como si no
Definiendo
m
(
i
,
j
)
Casoi>0 yj≥di
Recordemos quem(i,j) =“menor número de monedas
necesarias para pagar exactamente el montojcon
denominacionesd1,d2, . . . ,di.”
m(i,0) =0,
j >0⇒m(0,j) =∞,
i >0∧di >j>0⇒m(i,j) =m(i−1,j),
sij≥di hay dos posibilidades
la solución óptima no usa monedas de denominacióndi
m(i,j) =m(i−1,j)
la solución óptima usa una o más monedas de
denominacióndi
Definición recursiva de
m
(
i
,
j
)
Conclusión de estas últimas filminas:
m(i,j) = 0 j =0 ∞ j >0∧i =0 m(i−1,j) di >j>0∧i>0 min(m(i−1,j),1+m(i,j−di)) j ≥di >0∧i>0
Otras posibles definiciones que usan backtracking
Considerando el número exacto de monedas de denominacióndi
m(i,j) = 0 j =0 ∞ j >0∧i=0 minq∈{0,1,...,j÷di}(q+m(i−1,j−q∗di)) j >0∧i>0
Acá estamos considerando la posibilidad de usar 0 monedas
(q=0) de denominacióndi, 1 moneda (q =1) de
denominacióndi, etc. De todas esas posibilidades se elige la
Otras posibles definiciones que usan backtracking
Considerando cuál moneda de las disponibles se usa
m(i,j) = 0 j=0 1+mini0∈{1,2,...,i}∧d i0≤j(m(i 0,j−d i0)) j>0
Acá estamos considerando la posibilidad de usar 1 moneda de denominacióndi (i0 =i), 1 moneda de denominacióndi−1
(i0 =i−1), etc. De todas esas posibilidades se elige la que minimice el número total de monedas. Para evitar cálculos repetidos, se restringe la búsqueda a monedas de índice menor a los ya utilizados.
Primera definición recursiva de
m
(
i
,
j
)
m(i,j) = 0 j =0 ∞ j >0∧i =0 m(i−1,j) di >j>0∧i>0 min(m(i−1,j),1+m(i,j−di)) j ≥di >0∧i>0Primera solución, ahora en pseudocódigo
funcambio(d:array[1..n]of nat, i,j: nat)retr: nat ifj=0thenr:= 0
else ifi = 0thenr:=∞
else ifd[i] > jthenr:= cambio(d,i-1,j)
elser:= min(cambio(d,i-1,j),1+cambio(d,i,j-d[i]))
fi fi fi end fun
Segunda definición recursiva de
m
(
i
,
j
)
m(i,j) = 0 j =0 ∞ j >0∧i=0 minq∈{0,1,...,j÷di}(q+m(i−1,j−q∗di)) j >0∧i>0Segunda solución, ahora en pseudocódigo
funcambio(d:array[1..n]of nat, i,j: nat)retr: nat ifj=0thenr:= 0 else ifi = 0thenr:=∞ elser:= cambio(d,i-1,j) forq:= 1toj÷di do r:= min(r,q+cambio(d,i-1,j-q*d[i])) od fi fi end fun
Tercera definición recursiva de
m
(
i
,
j
)
m(i,j) = 0 j=0 1+mini0∈{1,2,...,i}∧d i0≤j(m(i 0,j−d i0)) j>0Tercera solución, ahora en pseudocódigo
funcambio(d:array[1..n]of nat, i,j: nat)retr: nat ifj=0thenr:= 0
elser:=∞
fori’:= 1toido
ifd[i’]≤jthenr:= min(r,cambio(d,i’,j-d[i’]))fi od
r:= r + 1
fi end fun
Otras posibilidades
No son éstas las únicas formas de resolver el problema usando backtracking.
Podríamos definir, por ejemplo,
m(i,j) =“menor número de monedas necesarias para
pagar exactamente el montoj con denominaciones
di+1,di+2, . . . ,dn.”
Obtendríamos, entre otras posibles definiciones recursivas, m(i,j) = 0 j=0 ∞ j>0∧i=n m(i+1,j) di >j>0∧i <n min(m(i+1,j),1+m(i,j−di)) j≥di>0∧i <n
Primera solución, ahora en pseudocódigo
¡Queremos las monedas!
funcambio(d:array[1..n]of nat, i,j: nat, s: list of nat)
retr: list of nat ifj=0thenr:= s
else ifi = 0thenr:= an∞list
else ifd[i] > jthenr:= cambio(d,i-1,j,s)
else if|cambio(d,i-1,j,s)|≤|cambio(d,i,j-d[i],sCd[i])|
thenr:= cambio(d,i-1,j,s)
elser:= cambio(d,i,j-d[i],sCd[i])
fi fi fi
Problema de la mochila
Tenemos una mochila de capacidadW.
Tenemosnobjetos no fraccionables de valorv1,v2, . . . ,vn
y pesow1,w2, . . . ,wn.
Se quiere encontrar la mejor selección de objetos para llevar en la mochila.
Por mejor selección se entiende aquélla que totaliza el mayor valor posible sin que su peso exceda la capacidad
Simplificación y generalización
Simplificamos el problema:sólo nos interesa por ahora hallar el mayor valor posible sin exceder la capacidad de la mochila,
no nos interesa saber cuáles son los objetos que alcanzan ese máximo.
Generalizamos el problema:
Sean 0≤i ≤ny 0≤j ≤W,
definimosm(i,j) =“mayor valor alcanzable sin exceder la
capacidadj con objetos 1,2, . . . ,i.”
La solución del problema original se obtiene calculando m(n,W).
Definición recursiva de
m
(
i
,
j
)
m(i,j) = 0 j=0 0 j>0∧i=0 m(i−1,j) wi>j>0∧i>0 max(m(i−1,j),vi+m(i−1,j−wi)) j≥wi >0∧i>0Otras posibilidades
Ofrece las mismas variantes que en el problema de la moneda,
el pasaje a pseudocódigo es similar,
la incorporación de información con los objetos que van en la mochila es también parecido (un poco más complicado que para el problema de las monedas).
Problema del camino de costo mínimo
Entre todo par de vértices
Tenemos un grafo dirigidoG= (V,A), con costos no negativos en las aristas,
se quiere encontrar, para cada par de vértices, el camino de menor costo que los une.
Simplificación y generalización
Simplificamos el problema:sólo nos interesa por ahora hallar el costo de cada uno de los caminos de costo mínimo.
no nos interesa saber cuáles son los caminos que alcanzan ese mínimo.
Generalizamos el problema:
Sean 1≤i,j ≤ny 0≤k ≤n,
definimosmk(i,j) =“menor costo posible para caminos de
i aj cuyos vértices intermedios se encuentran en el
conjunto{1,. . . ,k}.”
La solución del problema original se obtiene calculando
Definición recursiva de
m
k(
i
,
j
)
mk(i,j) =
L[i,j] k =0 min(mk−1(i,j),mk−1(i,k) +mk−1(k,j)) k ≥1
dondeL[i,j]es el costo de la arista que va deiaj, o infinito si no hay tal arista.
Conclusiones
Hemos visto soluciones a tres problemas. En general, muy ineficiente.
Por ejemplo, si queremos pagar el monto 90 con nuestros billetes con denominaciones 1, 5 y 10,
m(3,90) llama a m(2,90) y m(3,80), m(2,90) llama a m(1,90) y m(2,85),
m(2,85) llama a m(1,85) ym(2,80),
m(3,80) llama am(2,80)y m(3,70).
Se ve que m(2,80) se calcula 2 veces.
y muchos otros llamados se repiten, incluso varias veces. Esto vuelve los algoritmos exponenciales.