Universidad Da Vinci Dr. Vicente Cubells (vcubells@udavinci.edu.mx)
Temario Técnicas de diseño de algoritmos Algoritmos ávidos (avaros) Backtracking Branch-‐and-‐bound
Algoritmos ávidos… Dado un problema con n entradas el método consiste
en obtener un subconjunto de éstas que satisfaga una determinada restricción definida para el problema
Cada uno de los subconjuntos que cumplan las
r e s t r i c c i o n e s d i r e m o s q u e s o n s o l u c i o n e s prometedoras
Una solución prometedora que maximice o minimice
una función objetivo la denominaremos solución óptima
Algoritmos ávidos… Identificación del problema Un conjunto de candidatos, que corresponden a las n entradas del problema Una función de selección que en cada momento determine el candidato idóneo para formar la solución de entre los que aún no han sido seleccionados ni rechazados Una función que compruebe si un cierto subconjunto de candidatos es prometedor. Entendemos por prometedor que sea posible seguir añadiendo candidatos y encontrar una solución Una función objetivo que determine el valor de la solución hallada. Es la función que queremos maximizar o minimizar Una función que compruebe si un subconjunto de estas entradas es solución al problema, sea óptima o no
Algoritmos ávidos… Podemos resumir el funcionamiento de los algoritmos
ávidos en los siguientes puntos:
Para resolver el problema, tratará de:
encontrar un subconjunto de candidatos tales que, cumpliendo las restricciones del problema, constituya la solución óptima
Para ello
trabajará por etapas, tomando en cada una de ellas la decisión que le parece la mejor, No considera las consecuencias futuras Por tanto, escogerá de entre todos los candidatos el que produce un óptimo local para esa etapa, Supone que será a su vez óptimo global para el problema
Algoritmos ávidos… Podemos resumir el funcionamiento de los algoritmos
ávidos en los siguientes puntos:
Antes de añadir un candidato a la solución: Comprobará si es prometedora al añadirlo.
En caso afirmativo lo incluirá en ella y en caso contrario descartará este candidato No volverá a considerarlo
Cada vez que se incluye un candidato comprobará si el
conjunto obtenido es solución
Algoritmos ávidos…
Algoritmos ávidos.. Conjunto AlgoritmoAvido(Conjunto entrada) { Elemento x; Conjunto solucion; Boolean encontrada = false; crear(solucion); while (!EsVacio(entrada) && (! encontrada)) { x = SeleccionarCandidato(entrada); if (EsPrometedor(x,solucion)) { Incluir(x,solucion); if (EsSolucion(solucion)) { encontrada =true; } } } return solucion; }
Algoritmos ávidos… ¿Por qué no utilizar siempre algoritmos ávidos?
Algoritmos ávidos…
Misma estrategia
Algoritmos ávidos… Problema de la mochila Dados n elementos e1, e2, ..., en con pesos p1, p2, ..., pn y beneficios b1, b2, ..., bn, y dada una mochila capaz de albergar hasta un máximo de peso M (capacidad de la mochila), queremos encontrar las proporciones de los n elementos x1, x2, ..., xn (0 ≤ xi ≤ 1) que tenemos que introducir en la mochila de forma que la suma de los beneficios de los elementos escogidos sea máxima Esto es, hay que encontrar valores (x1, x2, ..., xn) de n forma que se maximice la sujeta a la restricción ∑ bi xi i =1
n
∑p x < M i i
i=1
Algoritmos ávidos… Problema de la mochila (Solución) Un algoritmo ávido que resuelve este problema ordena los elementos de forma decreciente respecto a su ratio b /p y va añadiendo objetos mientras exista espacio i
i
#define maxElem 10 struct Registro{ float peso, int beneficio }; struct Registro Elementos[maxElem] float Mochila[maxElem];
Algoritmos ávidos… Problema de la mochila (Solución) (* se supone que los elementos de "e" están en orden decreciente de su ratio bi/ pi *)
void Mochila(Elementos e; int n; float M; Mochila sol) { float peso_en_curso; int i; for (i=1 ; i < maxElem; ++i) { sol[i]=0.0; } peso_en_curso=0.0; i=1; while ((peso_en_curso < M) && (i<=n)) { if (e[i].peso + peso_en_curso) <= M) { sol[i]=1.0; } else { sol[i]=(M-peso_en_curso)/e[i].peso; } peso_en_curso = peso_en_curso + (sol[i] * e[i].peso); ++i; }
}
Algoritmos ávidos… Problema de la mochila (0,1) Consideremos una modificación al problema de la Mochila
en donde añadimos el requerimiento de que no se pueden escoger fracciones de los elementos, es decir, xi = 0 ó xi = 1, 1 ≤ i ≤ n. Como en el problema original, deseamos maximizar la n n cantidad b x sujeta a la restricción p x < M
∑ i =1
i i
∑
i i
i=1
¿Seguirá funcionando el algoritmo anterior en este caso?
Algoritmos ávidos… Problema de la mochila (0,1) (Solución) Supongamos una mochila de capacidad M = 6, y que disponemos de los siguientes elementos (ya ordenados respecto a su ratio beneficio/peso):
Algoritmos ávidos… Problema del fontanero diligente Un fontanero necesita hacer n reparaciones urgentes, y sabe de
antemano el tiempo que le va a llevar cada una de ellas: en la tarea i-‐ésima tardará ti minutos. Como en su empresa le pagan dependiendo de la satisfacción del cliente, necesita decidir el orden en el que atenderá los avisos para minimizar el tiempo medio de espera de los clientes. En otras palabras, si llamamos Ei a lo que espera el cliente i-‐ésimo hasta ver reparada su avería por completo, necesita minimizar la expresión: n
E (n) = ∑ Ei i =1
Algoritmos ávidos… Problema del fontanero diligente (Solución) El fontanero siempre tardará el mismo tiempo global
T = t1 + t2 + ... + tn en realizar todas las reparaciones, independientemente de la forma en que las ordene.
Los tiempos de espera de los clientes sí dependen de esta
ordenación En efecto, si mantiene la ordenación original de las tareas (1, 2, ..., n), la expresión de los tiempos de espera de los clientes viene dada por: E1 = t1 E2 = t1 + t2 ..... En = t1 + t2 + ... + tn
Algoritmos ávidos… Problema del fontanero diligente (Solución) Lo que queremos encontrar es una permutación de las tareas en donde se minimice la expresión de E(n) que, basándonos en las ecuaciones anteriores, viene dada por: n
n
i =1
i =1
E (n) = ∑ Ei = ∑ (n − i + 1)ti
Algoritmos ávidos… Problema del fontanero diligente (Solución) Vamos a demostrar que la permutación óptima es aquella en la que los avisos se
atienden en orden creciente de sus tiempos de reparación Para ello, denominemos X = (x1,x2,...,xn) a una permutación de los elementos (1,2,...,n), y sean (s1,s2,...,sn) sus respectivos tiempos de ejecución, es decir, (s1,s2,...,sn) va a ser una permutación de los tiempos originales (t1,t2,...,tn) Supongamos que no está ordenada en orden creciente de tiempo de reparación, es decir, que existen dos números xi < xj tales que si > sj Sea Y = (y1,y2,...,yn) la permutación obtenida a partir de X intercambiando xi con xj, es decir, yk = xk si k ≠ i y k ≠ j, yi = xj, yj = xi Si probamos que E(Y) < E(X) habremos demostrado lo que buscamos, pues mientras más ordenada (según el criterio dado) esté la permutación, menor tiempo de espera supone
Algoritmos ávidos Problema del fontanero diligente (Solución) Si probamos que E(Y) < E(X) habremos demostrado lo que buscamos, pues mientras
más ordenada (según el criterio dado) esté la permutación, menor tiempo de espera supone. Pero para ello, basta darse cuenta que: n
E (Y ) = (n − xi + 1)s j + (n − x j + 1)si +
∑ (n − k + 1)s
k =1, k ≠i , k ≠ j Por tanto:
E ( X ) − E (Y ) = (n − xi + 1)(si − s j ) + (n − x j + 1)(s j − si ) E ( X ) − E (Y ) = ( x j − xi )(si − s j ) > 0
k
Backtracking… Los métodos estudiados hasta ahora, construyen la solución basándose en
ciertas propiedades de la misma; en los algoritmos Ávidos: se va construyendo la solución por etapas, siempre avanzando sobre la solución parcial previamente calculada; en Programación Dinámica: dar una expresión recursiva de la solución si se verifica el principio de óptimo, y luego calcularla eficientemente Ciertos problemas no son susceptibles de solucionarse con ninguna de estas técnicas a través de un estudio exhaustivo de un conjunto conocido a priori de posibles soluciones, en las que tratamos de encontrar una o todas las soluciones y por tanto también la óptima
Backtracking… Se fundamenta en:
Recorrido en profundidad de un árbol Pueden suceder dos cosas
Éxito: Si se busca una sola solución, termina. Si busca varias soluciones o la mejor, continúa. Fracaso: No se encuentra una solución, regresar borrando los nodos parciales
Un problema puede resolverse con un algoritmo Backtracking cuando:
la solución puede expresarse como una n-‐tupla [x , x , ..., x ] donde cada una de las 1
2
n
componentes x de este vector es elegida en cada etapa de entre un conjunto finito de valores Cada etapa representará un nivel en el árbol de expansión i
En primer lugar debemos fijar la descomposición en etapas que vamos a
realizar y definir, dependiendo del problema, la n-‐tupla que representa la solución del problema y el significado de sus componentes x i
Una vez que veamos las posibles opciones de cada etapa quedará definida la
estructura del árbol a recorrer
Backtracking… Problema de las 8 reinas Disponemos de un tablero de ajedrez de tamaño 8x8, y se trata de colocar en él ocho reinas de manera que no se amenacen
según las normas del ajedrez, es decir, que no se encuentren dos reinas ni en la misma fila, ni en la misma columna, ni en la misma diagonal Numeramos las reinas del 1 al 8 Cualquier solución a este problema estará representada por una 8-‐tupla [x1,x2,x3,x4,x5,x6,x7,x8] en la que cada xi representa la columna donde la reina de la fila i-‐ésima es colocada. Una posible solución al problema es la tupla [4,6,8,2,7,1,3,5]
Backtracking… Problema de las 8 reinas (Análisis) Restricciones
Restricciones explícitas. Formadas por reglas que restringen los valores
que pueden tomar los elementos x a un conjunto determinado. En nuestro problema este conjunto es S = {1,2,3,4,5,6,7,8} Restricciones implícitas. Indican la relación existente entre los posibles valores de los x para que éstos puedan formar parte de una n-‐tupla solución i
i
En el problema que nos ocupa: dos reinas no pueden situarse en la misma columna y por tanto no puede haber dos x iguales dos reinas no pueden estar en la misma diagonal, lo cual reduce el número de opciones Esta condición se refleja en la segunda restricción implícita que, en forma de ecuación, puede ser expresada como: i
|x – x’| ≠ |y – y’|, siendo (x,y) y (x’,y’) las coordenadas de dos reinas en el tablero
Backtracking… Problema de las 8 reinas (Análisis) Para un tablero de 4x4
o
o o
[-‐,-‐,-‐,-‐]
[1,-‐,-‐,-‐]
[1,2,-‐,-‐]
Backtracking… Problema de las 8 reinas (Análisis) Para un tablero de 4x4
o o o
o
o
[1,3,2,-‐] o
[-‐,-‐,-‐,-‐]
[1,-‐,-‐,-‐]
[1,3,-‐,-‐]
Backtracking… Problema de las 8 reinas (Análisis) Para un tablero de 4x4
o
o
o o [-‐,-‐,-‐,-‐]
[1,-‐,-‐,-‐]
[1,3,-‐,-‐] o 0 [1,3,4,-‐]
Backtracking… Problema de las 8 reinas (Análisis) Para un tablero de 4x4
o o
o
o
o
[1,4,2,-‐]
o [-‐,-‐,-‐,-‐]
[1,-‐,-‐,-‐]
o
[1,4,-‐,-‐]
0 o o [1,4,2,3]
Backtracking… Problema de las 8 reinas (Análisis) Para un tablero de 4x4
o o
o
[-‐,-‐,-‐,-‐]
[2,-‐,-‐,-‐]
[2,1,-‐,-‐]
Backtracking… Problema de las 8 reinas (Análisis) Para un tablero de 4x4
o
o
o [-‐,-‐,-‐,-‐]
[2,-‐,-‐,-‐]
[2,3,-‐,-‐]
Backtracking… Problema de las 8 reinas (Análisis) Para un tablero de 4x4
o o
o [2,4,1,-‐]
o
o
o [-‐,-‐,-‐,-‐]
[2,-‐,-‐,-‐]
[2,4,-‐,-‐]
o o o
Solución!!!
o [2,4,1,3]
Backtracking… En resumen, podemos decir que Backtracking es
un método exhaustivo de tanteo (prueba y error) se caracteriza por un avance progresivo en la búsqueda
de una solución mediante una serie de etapas en dichas etapas se presentan unas opciones cuya validez ha de examinarse con objeto de seleccionar una de ellas para proseguir con el siguiente paso
Backtracking Esquema general de los algoritmos void VueltaAtras(etapa) { IniciarOpciones; do { SeleccionarNuevaOpcion; if (Aceptable) { AnotarOpcion; if (SolucionIncompleta) { VueltaAtras(etapa_siguiente); if (!exito) { CancelarAnotacion; } else { exito = true; } (* solucion completa *) } } while (!exito) && (!UltimaOpcion) }
Branch and Bound… Variante de Backtracking Difiere en: la posibilidad de generar nodos siguiendo distintas estrategias Backtracking hace un recorrido en profundidad del árbol que representa el espacio de soluciones Branch and Bound
versión más sencilla puede seguir un recorrido en anchura (estrategia LIFO) o en profundidad (estrategia FIFO) o utilizando el cálculo de funciones de coste para seleccionar el nodo que en principio parece más prometedor (estrategia de mínimo coste o LC)
Branch and Bound Etapas principales del algoritmo: Selección: se encarga de extraer un nodo de entre el conjunto de los nodos vivos. La forma de escogerlo va a depender directamente de la estrategia de búsqueda que decidamos para el algoritmo Ramificación: se construyen los posibles nodos hijos del nodo seleccionado en el paso anterior Poda: en la que se eliminan algunos de los nodos creados en la etapa anterior
Esto contribuye a disminuir en lo posible el espacio de búsqueda y así atenuar la complejidad de estos algoritmos basados en la exploración de un árbol de posibilidades. Aquellos nodos no podados pasan a formar parte del conjunto de nodos vivos, y se comienza de nuevo por el proceso de selección.
El algoritmo finaliza cuando encuentra la solución, o bien cuando se
agota el conjunto de nodos vivos
Resumiendo Algoritmos ávidos buscan la solución de cada etapa de
manera individual y siempre la consideran como la más óptima sin importar el “futuro” Backtracking es un método exhaustivo de tanteo (prueba y error) Recorrido en profundidad
Branch and Bound es una variante de Backtracking Recorrido en profundidad, en amplitud o en base a una función
de coste