Tema 4y 5. Algoritmos voraces. Algoritmos sobre grafos Objetivos: Estudio de la técnica de diseño de algoritmos voraces Estudio de algunos problemas clásicos: Indice: Mochila con fraccionamiento Algoritmos de Prim y Kruskal Algoritmo de Dijkstra Introducción: Esquema Voraz El problema del árbol de recubrimiento de coste mínimo: algoritmos de Kruskal y Prim El problema del camino mínimo en un grafo: algoritmo de Dijkstra 1
Bibliografía Introduction to Algorithms Cormen, Leiserson, Rivest. Sección 17.2, capítulos 24 y 25 Fundamentos de Algoritmia Brassard, Bratley Capítulo 6 2
1. Introducción. Esquema Voraz Se aplica normalmente a problemas de optimización: Búsqueda del valor óptimo (máximo o mínimo) de una cierta función objetivo En un dominio determinado (restricciones del problema) Ejemplo: problema de la mochila con fraccionamiento, cambio de monedas. La solución se puede presentar como una secuencia de decisiones Medida local de optimización Las decisiones son irreversibles No siempre encuentra la solución óptima, pero en ocasiones permite encontrar una solución aproximada con un coste computacional bajo. 3
Ejemplo: Cajero automático Suministrar la cantidad de billetes solicitada de forma que el número total de billetes sea mínimo. Ejemplo: M=11000 pts, B={1000, 2000, 5000} 11x1000 (11 billetes) 5x2000+1x1000 (6 billetes) 2x5000+1x1000 (3 billetes) La estrategia voraz consiste en coger el billete más grande: A veces no hay solución; p.e. Si M=3000 y no hay billetes de 1000 A veces no la encuentra; p.e. M=11000 y no hay billetes de 1000 A veces encuentra una factible pero no óptima; p.e. B={100, 500, 1100, 5000} y M=6500. La solución que se obtendría sería: 1x5000+1x100+4x100 (6 billetes) 4
Elementos de un algoritmo voraz C S Solucion(S) Factible(S) Selección(C) f Conjunto de elementos o candidatos Conjunto de elementos de la solución en curso Es S solución? Es S completable o factible? Seleccionar un candidato Función objetivo 5
Esquema Voraz Algoritmo Voraz: Obtiene un subconjunto de C que optimiza la función objetivo. Para ello: Seleccionar en cada instante un candidato (criterio local de optimización) Añadir el candidato a la solución en curso si es completable (o factible), sino rechazarlo Repetir los dos puntos anteriores hasta obtener la solución (o no quedan más candidatos) Recordemos... Un algoritmo voraz no siempre proporciona la solución óptima Las decisiones son irreversibles 6
Esquema Voraz. Algoritmo EsquemaVoraz (C) S:= ; mientras not (Solución(S)) (C ) hacer x:=selección(c); C:=C-{x}; Si Factible(S {x}) entonces S:=S {x}; fmientras Si Solución(S) entonces devuelve S sino devuelve No hay solución 7
El problema de la mochila con fraccionamiento Sea una mochila de capacidad M y N objetos. Cada objeto tiene un volumen w i y un valor p i, 1 i N. Problema: Qué objetos debemos introducir en la mochila para que el valor total sea máximo? Se permite que se fraccionen los elementos. Datos: (p 1.. p N ), (w 1.. w N ), M Resultado: (x 1.. x N ), 0 x i 1 Obtener el máximo beneficio con la restricción de que los objetos quepan en la mochila N i= 1 p i x i N i= 1 w i x i M 8
Mochila con fraccionamiento (cont) Ejemplo: M=20, w=(18,15,10) p=(25,24,15) Soluciones factibles: SOLUCION PESO VALOR a) (1/2, 1/3, 1/4) 16.50 24.25 b) (1, 2/15, 0) 20.00 28.20 c) (0, 2/3, 1) 20.00 31.00 d) (0, 1, 1/3) 20.00 31.50 La solución óptima debe llenar la mochila completamente. Sólo se fracciona el último. Criterio de selección de los objetos que se van metiendo: - El de menor peso - El de mayor valor - El de mayor relación valor/peso 9
Mochila con fraccionamiento (cont) Ejemplo: M=50, w=(30,18,15,10) p=(25,13,12,10) - Selección: El de menor peso Solución= (7/30, 1, 1, 1) Beneficio= 7/30*25+13+12+10=39.6; Peso=50; - Selección: El de mayor valor Solución= (1, 1, 2/15, 0) Beneficio= 25+13+2/15*12=40.83; Peso=50; - Selección: El de mayor relación valor/peso p i / w i =(5/30, 13/18,12/15,10/10) Solución= (1, 0, 10/15, 1) Beneficio= 25+10/15*12+10=42.99; Peso=50; 10
Mochila con fraccionamiento (cont) Mochila-continua (p,w,m) C:={1,2,..,N} para i:=1 hasta N hacer solución[i]:=0 fpara; mientras (C ) and (M>0) hacer i:= elemento de C con máximo p i / w i C:= C-{i} si w i M entonces M:=M- w i ; solución [i]:=1 sino M:=0 ; solución [i]:=m/ w i fsi fmientras devuelve solución 11
Mochila con fraccionamiento (cont) Coste del algoritmo: Versión 1) Cada vez que se selecciona un objeto se recorre todo el vector para encontrar el de máxima p i / w i. El coste de selección sería O (N) y el del algoritmo O(N 2 ). Versión 2) Los objetos se ordenan previamente O(N logn); el coste de selección sería O(1) y el del algoritmo O(N logn). Versión 3) Se puede utilizar un max-heap para representar el conjunto de objetos. El coste del algoritmo es O(N logn). En qué ocasiones es mejor esta última opción? 12
Problema AEM (i) Problema: Establecer una red eléctrica entre ciertas ciudades de forma que el coste de la red sea mínimo Modelo: G=(V,E,w) un grafo no dirigido conexo y ponderado V conjunto de ciudades E enlaces factibles w coste de los enlaces (no negativos) Ejemplo: a 4 8 11 b h 7 8 7 c 2 i 4 1 g 2 d f 14 9 10 e
Problema AEM (ii) La red eléctrica deseada será un subconjunto de arcos T de E, sin ciclos que conecte todos los vértices y cuyo peso total w(t)= (u,v) Τ w(u,v) sea mínimo Ejemplo: a 4 8 11 b h 7 8 7 c i 1 g 4 2 d f 14 9 2 Peso total=37 La solución no es única: (a,h) en lugar de (b,c) 10 e
Problema AEM (iii) Dado un grafo no dirigido y conexo G=(V,E), y ponderado con w:e R + Encontrar T E: G =(V,T) es un subgrafo conexo y acíclico de (V,E) la suma de los pesos de los arcos de T sea mínima T es acíclico, conexo y no dirigido, entonces es un árbol Se debe extender por todos los vértices La suma de los pesos de sus arcos debe ser mínima Árbol de extensión de coste mínimo
Estrategia voraz para la obtención de un AEM Algoritmo Construcción de un AEM Datos G=(V,E,w)/*no dirigido,conexo,ponderado*/ Resultado T E /*árbol de extensión de peso mínimo*/ Método:/*I: T es un subconjunto de un AEM*/ T:= ; ; mientras T no sea AEM hacer /* E es el conjunto de candidatos*/ (u,v):=selección(e); E:=E-{(u,v)} Si FACTIBLE(T {(u,v)}) /*un cjto de arcos es factible sino tiene ciclos*/ entonces T:=T {(u,v)}; fmientras
Algoritmo de Kruskal T:= ; Para todo v V[G hacer MAKE_SET(v); Ordenar los arcos de E por pesos repetir (u,v):= MINIMO(E); compu:=find(u); compv:=find(v); si compu<>compv entonces MERGE(compu,compv); T:=T {(u,v)}; hasta que T tenga n-1 arco
Algoritmo de Dijkstra Resuelve el problema de los caminos más cortos con un solo origen sobre un grafo dirigido y ponderado en el que los pesos de los arcos son no negativos. Algoritmo voraz: Empezando en el vértice origen s, construir de forma incremental caminos a los demás vértices seleccionando en cada paso un vértice v no seleccionado anteriormente tal que: Exista algún vértice u V ya seleccionado previamente tal que (u,v) E Al añadir (u.v) al camino que terminaba en u se produzca el menor incremento de peso posible. 18
Algoritmo de Dijkstra El algoritmo mantiene un conjunto de vértices S cuyo peso del camino más corto desde el origen s ya es conocido. El algoritmo va seleccionando el vértice u V-S con la mejor estimación del camino mínimo. Este vértice u se añade a S y se actualiza los mejores caminos desde s a los vértices de S. Se utiliza un vector auxiliar D para guardar la longitud del camino más corto que llega a cada vértice del grafo pasando únicamente por vértices de S. Al final del algoritmo S=V y el vector D contiene las distancias más cortas desde el origen a cada uno de los demás vértices. 19
Algoritmo de Dijkstra u 50 v s 10 100 30 10 20 w x 60 Vector D u 10 v inf. w 30 x 100 20
Algoritmo de Dijkstra u v s 10 100 30 x w Vector D u 10 v inf. w 30 x 100 21
Algoritmo de Dijkstra u 50 v s 10 100 30 x w Vector D u 10 v 60 w 30 x 100 22
Algoritmo de Dijkstra 10 s Vector D u 10 u 30 x v 50 v 20 w 60 w 30 x 90 23
Algoritmo de Dijkstra 10 s Vector D u 10 u 30 vx v 50 10 w 30 v 20 w x 60 24
Dijkstra(V,E,s,p) Algoritmo de Dijkstra {D[v]:coste mínimo de ir desde s a v pasando únicamente por nodos de S} Para cada v V hacer D[v]:= ; D[s]:=0;S:= ; C:=V; mientras C<> hacer u:= BorraMínimo(C); {mínimo según D} S:=S {u}; Para cada v Lady[u] hacer D[v]:= min(d[v],d[u]+p(u,v)) devuelve D 25
Traza del algoritmo de Dijkstra s 10 u 2 3 1 9 4 v 6 5 x 7 2 y 26
Dijkstra(V,E,s,p) Algoritmo de Dijkstra {D[v]:coste mínimo de ir desde s a v pasando únicamente por nodos de S} Para cada v V hacer D[v]:= ; D[s]:=0;S:= ; C:=V; mientras C<> hacer u:= BorraMínimo(C); {mínimo según D} S:=S {u}; Para cada v Lady[u] hacer D[v]:= min(d[v],d[u]+p(u,v)) devuelve D Cómo se implementa C?: - Un Vector - Un MinHeap 27
Algoritmo de Dijkstra. Análisis de costes (i) Si C se implementa como un vector y G mediante listas de adyacencia: Existen V operaciones de obtener el mínimo en el vector con un coste O( V ) Cada vértice v de V se inserta en S exactamente una vez, de forma que cada arco en la lista de adyacencia se examina una única vez (bucle Para cada v Lady[u] hacer ). Debido a que el número total de arcos en G es E, existen E iteraciones de este bucle, y cada iteración tiene un coste O(1) El coste total del algoritmo es O( V 2 + E )=O( V 2 ) 28
Algoritmo de Dijkstra. C es un Heap. Dijkstra(V,E,s,p) {D[v]:coste mínimo de ir desde s a v pasando únicamente por nodos de S} para cada v V hacer D[v]:= ; D[s]:=0;S:= ; C:=ConstuirHeap(V,D); mientras C<> hacer u:=eliminamin(c); {mínimo según D} S:=S {u}; para cada v Lady[u] hacer if D[v]>D[u]+p(u,v) entonces D[v]:=D[u]+p(u,v); C:=Modifica_Heap(D[v]); devuelve D 29
Algoritmo de Dijkstra. Análisis de costes (ii) Si C se implementa como un MinHeap y G mediante listas de adyacencia: Existen V operaciones de EliminaMin con un coste O(log V ). Hay que añadir el tiempo de ConstruirHeap, que es O( V ). En cada iteración del bucle Para cada v Lady[u] hacer, se deberá modificar D[v] y por lo tanto el Heap, con un coste O(log V ). EL número de veces que se ejecuta el bucle es O( E ) El coste total del algoritmo es O( V + E log V ) = O( E log V ) 30
Obtención de los caminos más cortos Utilizar un vector P de talla V para almacenar el predecesor de cada vértice en el camino. P[u] es el predecesor de u en el camino más corto desde el vértice origen hasta el vértice u. 31
Obtención de los caminos más cortos Dijkstra(V,E,s,p) {D[v]:coste mínimo de ir desde s a v pasando únicamente por nodos de S} {P[v]: predecesor de v en el camino más corto} para cada v V hacer D[v]:= ; P[v]:=NIL; D[s]:=0;S:= ; C:=V; mientras C<> hacer u:=borramínimo(c); {mínimo según D} S:=S {u}; para cada v Lady[u] hacer if D[v]>D[u]+p(u,v) entonces D[v]:=D[u]+p(u,v); P[v]:=u; devuelve (D,P) 32