Analisis de Tecnicas Algoritmicas

March 25, 2018 | Author: Jorginho_2 | Category: Graph Theory, Dynamic Programming, Mathematical Optimization, Algorithms, Decision Making


Comments



Description

5 Metodología y Tecnología de la Programación IIA n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Fuerza Bruta Fuerza Bruta f La técnica de FUERZA BRUTA es la que se utiliza para resolver un problema siguiendo la estrategia más obvia de solución. f Generalmente suele comportar más operaciones de las necesarias para resolver el problema, por lo suelen tener un coste bastante alto. f Por el contrario, suelen ser bastante sencillos de implementar. f Suelen utilizarse para problemas de tamaños pequeños. f Algunos ejemplos de algoritmos por fuerza bruta son: – El algoritmo de búsqueda lineal. – El algoritmo ordenación por el método de selección. 6 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Ejemplo Búsqueda Binaria Ejemplo Búsqueda Binaria f Ya vimos una función en C++ para buscar un número en un vector de enteros de tamaño n: f El algoritmo utiliza la fuerza bruta para buscar el número mirando uno por uno todos los elementos del vector, incluso si el vector estuviese ordenado. f También vimos que el coste de este algoritmo es O(n). f Aunque no es la forma óptima de buscar un elemento en un vector ordenado, cuando el tamaño del vector es pequeño resulta bastante rápido. int buscar(const int a[n], int x){ for (int i=0; i<n; i++){ if (a[i]==x) return i; } return -1; } 7 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Ejemplo Ordenación por Inserción Ejemplo Ordenación por Inserción f La siguiente función es una implementación en C++ del algoritmo de ordenación por Inserción void ordenacion_insercion(int a[],int n){ int aux; for(int i=1;i<n;i++){ aux=a[i]; for(j=i-1;j>=0;j--){ if(aux>array[j]){ array[j+1]=aux; break; } else array[j+1]=array[j]; } if(j==-1) array[0]=aux; } } 8 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Ejemplo Ordenación por Inserción Ejemplo Ordenación por Inserción f Este algoritmo divide el vector en dos partes, una con elementos ya ordenados y la otra con elementos todavía sin ordenar. f Inicialmente la parte ordenada está vacía y la que no está ordenada contiene todos los números. f El algoritmo utiliza la fuerza bruta para ir tomando uno por uno los elementos de la parte ordenada y los va insertado en la parte ordenada en el lugar que les corresponde. f Resulta sencillo comprobar que este algoritmo tiene un coste temporal O(n 2 ). 9 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Ejemplo de Ordenación por Inserción Ejemplo de Ordenación por Inserción 6 3 9 5 i=1 i aux 3 6 9 5 i 9 aux i=3 6 3 9 5 i=1 i 3 aux j 3 6 9 5 i 5 aux i=3 j 6 6 9 5 i 3 aux j i=1 3 6 9 9 i 5 aux i=3 j 3 6 9 5 i 3 aux i=2 3 6 6 9 i 5 aux i=3 j 3 6 9 5 i 9 aux i=2 j 3 5 6 9 i 5 aux i=3 j Parte ordenada Parte desordenada 10 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Divide y Vencerás Divide y Vencerás f La técnica de DIVIDE Y VENCERÁS consiste básicamente en descomponer el problema inicial en varios subproblemas del mismo tipo pero más sencillos, resolver los subproblemas y combinar sus soluciones para obtener la solución del problema inicial. f A su vez, para resolver cada uno de los subproblemas puede volver a aplicarse la técnica de divide y vencerás recursivamente para descomponerlo en otros subproblemas de menor complejidad, y así hasta que los subproblemas obtenidos tengan una solución trivial. 11 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Algoritmo Genérico de Divide y Vencerás Algoritmo Genérico de Divide y Vencerás Donde: n es el tamaño del problema, n 0 es el umbral de tamaño por debajo del cual el problema es trivial n i son los tamaños de los subproblemas en que se descompone el problema inicial. s i son las soluciones de los subproblemas de tamaño n i s es la solución del problema inicial. funcion divide_venceras(n) si n<n 0 s=resolver(n) si_no [n 1 ,…,n k ]=descomponer(n) desde i=1 hasta k hacer s i =divide_venceras(n i ) s=combinar(s1,…,sk) fin_si devolver s fin_funcion 12 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Condiciones Necesarias para Aplicar Divide y Vencerás Condiciones Necesarias para Aplicar Divide y Vencerás f Los problemas resolubles mediante la técnica de divide y vencerás tienen que cumplir una serie de condiciones: – Es necesario que el problema admita una formulación recursiva. – El problema inicial debe poder resolverse mediante la combinación de las soluciones de los subproblemas. – Los subproblemas deben ser del mismo tipo que el problema inicial pero con un tamaño estrictamente menor. 13 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Condiciones Deseables para Aplicar Divide y Vencerás Condiciones Deseables para Aplicar Divide y Vencerás f Además de las condiciones anteriores, si queremos que el algoritmo sea eficiente, debemos exigir las condiciones: – El tamaño de los subproblemas debe ser los más parecido posible y debe decrecer en progresión geométrica, es decir, debe ser n/c con c>0 una constante. – Nunca se debe resolver un mismo subproblema más de una vez – Las operaciones de descomponer y de combinar deben costar poco. – Hay que evitar generar nuevas llamadas cuando el tamaño de los subproblemas es suficientemente pequeño. Por tanto, hay que elegir bien el umbral n 0 . 14 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Coste del Algoritmo Divide y Venceras Coste del Algoritmo Divide y Venceras f Suponiendo que la función de descomposición origina k subproblemas de tamaño n/c, el coste del algoritmo divide y vencerás es la suma de – El tiempo que tarda en descomponer el problema inicial t d (n) – El tiempo que tarda en resolver los k suproblemas en que se divide el problema original kt(n/c) – El tiempo que tarda en combinar las soluciones de los subproblemas t c (n) es decir t(n) = kt(n/c) + t d (n) + t c (n) 15 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Coste del Algoritmo Divide y Venceras Coste del Algoritmo Divide y Venceras f Si la descomposición y la combinación no son excesivamente costosas, tendrán a lo sumo un coste polinómico, es decir t(n) = kt(n/c) + O(n i ) f Por tanto la solución a esta ecuación recursiva dependerá de k, c e i: – Si k<c i entonces t(n) ∈ O(n i ). – Si k=c i entonces t(n) ∈ O(n i log n). – Si k>c i entonces t(n) ∈ O(n log c k ). f Así pues, la utilización de divide y vencerás no garantiza nada acerca de la eficiencia del algoritmo obtenido. Puede suceder que el coste empeore, se mantenga o mejore respecto al de un algoritmo iterativo para el mismo problema. 16 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Ejemplos de Divide y Vencerás Ejemplos de Divide y Vencerás f Algunos algoritmos conocidos que utilizan la técnica de divide y vencerás son: – El algoritmo de Karatsuba y Ofman para la multiplicación de enteros grande. – El algoritmo de Strassen para el producto de matrices. – El algoritmo de búsqueda binaria. – El algoritmo de ordenación rápida Quicksort. 17 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Ejemplo Búsqueda Binaria Ejemplo Búsqueda Binaria f La siguiente función es una implementación en C++ de la búsqueda binaria recursiva de un elemento en un vector ordenado int busqueda_binaria(int x,const int a[],int i,int j){ int k; if (i>j) return -1; else{ k=(i+j)/2; if (a[k]==x) return k; else if(a[k]>x) return busqueda_binaria(x,a,i,k-1); else return busqueda_binaria(x,a,k+1,j); } } 18 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Ejemplo de Búsqueda Binaria Ejemplo de Búsqueda Binaria 2 3 5 9 12 18 25 32 37 41 48 50 i=7 j=12 a busqueda_binaria(25,a,7,12) (i+j)/2=9 32>18 2 3 5 9 12 18 25 32 37 41 48 50 (i+j)/2=i=7 j=8 a busqueda_binaria(25,a,7,8) 25<37 2 3 5 9 12 18 25 32 37 41 48 50 i=1 j=12 a busqueda_binaria(25,a,1,12) (i+j)/2=6 25=25 19 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Coste de la Búsqueda Binaria Coste de la Búsqueda Binaria f En la búsqueda binaria, el problema principal se descompone en 1 subproblema de tamaño n/2. Por otro lado, el coste de la descomposición y la combinación de soluciones es constante, así que el coste total es t(n) = t(n/2) + O(1) Resolviendo la ecuación recursiva tenemos f Por tanto, el algoritmo de busqueda binaria es más eficiente que el de búsqueda lineal. ) (log ) 1 ( O log 1 ) 1 ( O log ) 1 ( ) 1 ( O log ) 2 / ( ) 1 ( O ) 1 ( O ) 4 / ( ) 1 ( O ) 2 / ( ) ( 2 2 2 log 2 n O n n t n n t n t n t n t n ∈ + = = + = + = = + + = + = L 20 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Ejemplo Multiplicación de Enteros Grandes Ejemplo Multiplicación de Enteros Grandes f Todos conocemos el algoritmo para multiplicar enteros que nos enseñaron en el colegio f El coste de este algoritmo es O(n 2 ). f ¿Se pueden multiplicar dos enteros con un conste menor? x * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * n 2 * * * * * * * * * * * * * 21 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Multiplicación de Enteros Grandes mediante Divide y Vencerás Multiplicación de Enteros Grandes mediante Divide y Vencerás f Supongamos que queremos multiplicar dos enteros x e y de tamaño n expresados en una base i. f Se comprueba fácilmente que xy = ac i n + (ad+bc) i n/2 + bd y de esta forma, se puede calcular xy mediante 4 productos entre enteros de tamaño n/2. f El coste que se obtiene es t(n) = 4t(n/2) + O(n) a b c d n/2 n/2 n dígitos x y x=ai n/2 +b y=ci n/2 +d 22 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Calculo del Coste de la Multiplicación de Enteros Grandes Calculo del Coste de la Multiplicación de Enteros Grandes f Resolviendo la ecuación mediante deplegado tenemos t(n) n t(n/2) t(n/2) t(n/2) t(n/2) = n/2 t(n/4)t(n/4)t(n/4)t(n/4) = n/2 t(n/4)t(n/4)t(n/4)t(n/4) = n/2 t(n/4)t(n/4)t(n/4)t(n/4) = n/2 t(n/4)t(n/4)t(n/4)t(n/4) = … … … … … … … … … … … … … … … … 23 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Calculo del Coste de la Multiplicación de Enteros Grandes Calculo del Coste de la Multiplicación de Enteros Grandes f Resolviendo la ecuación mediante deplegado tenemos … … … … n n/2 n/4 n/4 n/4 n/4 n/2 n/4 n/4 n/4 n/4 n/2 n/4 n/4 n/4 n/4 n/2 n/4 n/4 n/4 n/4 … … … … 111111111111111111111111111111111111111111111111 log 2 n = 16n/4 = 4n/2 = 1n = 4 i n/2 i = 4 log 2 n n/2 log 2 n Nivel 4 i copias de n/2 i 0 1 2 i + + + + ∑ = n i i i n 2 log 0 2 / 4 24 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Mejora del Algoritmo de Multiplicación de Enteros Grandes Mejora del Algoritmo de Multiplicación de Enteros Grandes f Así pues, tenemos un coste total de modo que este algoritmo, a pesar de aplicar la técnica de divide y vencerás no es más eficiente que el anterior. f ¿Se puede construir un algoritmo mediante divide y vencerás más eficiente que el anterior? f Karatsuba y Ofman desarrollaron un algoritmo más eficiente a partir de la misma descomposición anterior pero basándose en la propiedad xy = ac i n + ((a-b)(d-c)+ac+bd) i n/2 + bd f De este modo, sólo se realizan 3 productos de enteros de tamaño n/2. f El coste de este algorimo es t(n) = 3t(n/2) + O(n) ∈ O(n 1.57 ) ) ( O 2 2 ) 2 / 4 ( 2 / 4 2 / 4 ) ( 2 2 log log 0 log 0 log 0 log 0 2 2 2 2 2 n n n n n n n n t n n i i n i i n i i i n i i i ∈ = = = = = = ∑ ∑ ∑ ∑ = = = = 25 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Multiplicación de Enteros Grandes (Karatsuba y Ofman) Multiplicación de Enteros Grandes (Karatsuba y Ofman) f Suponiendo que x e y son enteros de n dígitos, el algoritmo de Karatsuba y Ofman puede escribirse así f Para que el algoritmo sea más eficiente debe fijarse el umbral en n 0 =500 dígitos. funcion productoK&O(enterogrande x,enterogrande y) si n=1 devolver xy si_no (a,b)=descomponer(x) (c,d)=descomponer(y) ac=productoK&O(a,c) bd=productoK&O(b,d) abdc=productoK&O(a-b,d-c) devolver ac*i n + (abdc+ac+bd)*i n /2 + bd fin_si fin_funcion 26 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Algoritmos Voraces Algoritmos Voraces f La técnica VORAZ consiste en resolver un problema de manera gradual tomando decisiones o realizando acciones que permiten ir construyendo progresivamente la solución del problema. f Con cada decisión tomada se obtiene una solución parcial del problema y se reduce la dimensionalidad del mismo. f Suele utilizarse en problemas de optimización. 27 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Condiciones Necesarias para Aplicar la Técnica Voraz Condiciones Necesarias para Aplicar la Técnica Voraz f Debe existir un función que hay que maximizar o minimizar conocida como FUNCIÓN OBJETIVO. f La función objetivo depende de varias variables que toman valores en un conjunto conocido como DOMINIO. f Existe una función, conocida como FUNCIÓN SOLUCIÓN, que permite saber si unos determinados valores de las variables son o no solución del problema. La asignación de un valor a una variable se denomina DECISIÓN. f Existe un conjunto de restricciones que deben satisfacer los valores de las variables de la función objetivo y existe una función para saber si las decisiones tomadas hasta el momento satisfacen o no las restricciones. Esta función se suele llamar FACTIBLE, y el conjunto de decisiones factibles tomadas hasta el momento se llama SOLUCIÓN EN CURSO. 28 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Funcionamiento de la Técnica Voraz Funcionamiento de la Técnica Voraz f La técnica voraz intenta resolver el problema fijando progresivamente los valores de las variables de la función objetivo, sin violar las restricciones, hasta llegar al máximo o mínimo de función. f En cada paso se determina el valor de una de las variables aún no fijada mediante una FUNCIÓN DE SELECCIÓN. En cada instante, la función de selección siempre toma la decisión factible que es localmente óptima, es decir, la que hace que la función objetivo tome el mejor valor posible hasta el momento, sin intentar averiguar si forma parte de la solución óptima global. f Una de las características que diferencian a la técnica voraz de otras que estudiaremos después es que ¡nunca reconsidera una decisión tomada! 29 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Algoritmo Genérico Voraz Algoritmo Genérico Voraz f Suponiendo que el dominio de las variables de la función objetivo fuese C, podemos expresar el algoritmo voraz así: funcion voraz(conjunto_candidatos c) s=∅ mientras(no solucion(s) ∧ no vacio(c)) x=seleccion(c) c=c-{x} si factible(s∪{x}) entonces s=s∪{x} fin_mientras si solucion(s) entonces devoler s si_no devolver ∅ fin_funcion 30 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Coste Temporal del Algoritmo Voraz Coste Temporal del Algoritmo Voraz f El coste de un algoritmo voraz depende de dos factores: – El número de iteraciones del bucle, que depende del tamaño de la solución construida y del tamaño del conjunto de candidatos. – El coste de las funciones seleccion y factible. La función factible suele tener un coste constante, pero la función seleccion ha de explorar el conjunto de candidatos y depende de su tamaño. 31 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Ventajas e Inconvenientes de los Algoritmos Voraces Ventajas e Inconvenientes de los Algoritmos Voraces f La principal ventaja de los algoritmos voraces es que suelen ser bastante eficientes. f Sin embargo, puesto que siempre toman decisiones localmente óptimas y no reconsideran nunca las decisiones tomadas, no garantizan que se obtenga la solución óptima del problema, ni siquiera que se obtenga una solución, aún cuando dicha solución exista. f Esto es debido a que las decisiones localmente óptimas no garantizan la obtención de la solución óptima global. f Los problemas resolubles mediante la técnica voraz, deben cumplir el principio de optimalidad: “Una secuencia óptima de decisiones, toda subsecuencia ha de ser también óptima.” 32 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Ejemplos de Algoritmos Voraces Ejemplos de Algoritmos Voraces f Algunos problemas conocidos que pueden resolverse mediante la técnica voraz son: – El problema del cambio en monedas. – El problema de los códigos de Huffman. – El problema de los tiempos de espera mínimos. – El problema de la mochila. – El problema de los caminos mínimos en grafos. 33 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Cambio de Monedas El Problema del Cambio de Monedas f Dado un conjunto C de monedas de n tipos, se trata de encontrar el número mínimo de monedas que sumen una cantidad x. f Este problema satisface el principio de optimalidad, ya que puede demostrarse que si se tiene una solución óptima S={e 1 ,…,e k } para el problema de sumar una cantidad x, entonces, S-{e i } también es la solución óptima para sumar la cantidad x-e i . 34 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Cambio de Monedas Elementos del Algoritmo Voraz El Problema del Cambio de Monedas Elementos del Algoritmo Voraz f Función objetivo: El número de monedas, que hay que minimizar. f Dominio o conjunto de candidatos: El conjunto C formado por todas las monedas de que disponemos. f Función solución: S={e 1 ,…,e k } es solución si Σ i e i = x. f Función factible: S={e 1 ,…,e k } es factible si Σ i e i ≤ x. f Función selección: Elegir si es posible la moneda de mayor valor de C. 35 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Cambio de Monedas Algoritmo Voraz El Problema del Cambio de Monedas Algoritmo Voraz f El siguiente algoritmo implementado en C++ utiliza la técnica voraz para resolver el problema del cambio de monedas: bool cambio(int x,const int valor[n],int solucion[n]){ for (int i=0;i<n;i++) solucion[i]=0; int i=0, suma=0; while (suma<x && i<n) if (suma+valor[i]<=x){ solucion[i]++; suma+=valor[i];} else i++; if (suma==x) return true; else{ for (int i=0;i<n;i++) solucion[i]=0; return false;} } 36 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Cambio de Monedas Optimalidad El Problema del Cambio de Monedas Optimalidad f El algoritmo anterior no siempre proporciona la solución óptima. f Ejemplo. Si tenemos 5 monedas de valores valor[5]={50,25,20,5,1} y queremos sumar x=42, el algoritmo nos daría la solución s[5]={0,1,0,3,2}, es decir, 6 monedas, mientras que la solución óptima es s[5]={0,0,2,0,2}, que son 4 monedas. f Además, en caso de que C no contenga la moneda unidad, ni siquiera se garantiza que el problema tenga la solución. f Para que el algoritmo alcance la solución óptima es necesario que C esté formado por tipos de monedas que sean potencia de un tipo básico t (por ejemplo, C={125,25,5,1}). En este caso, el problema se reduce a encontrar la descomposición de x en base t, que es única y mínima. 37 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Cambio de Monedas Coste Temporal El Problema del Cambio de Monedas Coste Temporal f Para que la función de selección de la función cambio funcione adecuadamente, el vector de valores de las monedas debe estar ordenado en orden decreciente. f El coste de este algoritmo es de O(max(nlog n, m)) donde n es el número de tipos de monedas de C, y m es el número de iteraciones que realiza el bucle. 38 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema de la Mochila El Problema de la Mochila f Se dispone de una colección de n objetos o 1 ,…,o n , cada uno de ellos con un peso p i y un valor v i asociados. f Por otro lado se tiene una mochila capaz de soportar un peso máximo p max . f El problema consiste en maximizar el valor de los objetos que se introduzcan en la mochila sin sobrepasar p max . f Dependiendo de si los objetos se pueden fraccionar o no, existen dos variantes del problema: – Mochila fraccionada: Los objetos se pueden fraccionar, es decir se puede colocar en la mochila trozos de objetos. – Mochila entera: Los objetos no se pueden fraccionar y por tanto deben ir enteros en la mochila. f Ambas versiones satisfacen el principio de optimalidad pero sólo el primero puede resolverse con la técnica voraz. 39 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Problema de la Mochila Fraccionada Elementos del Algoritmo Voraz Problema de la Mochila Fraccionada Elementos del Algoritmo Voraz f Puesto que los objetos se pueden fraccionar, llamaremos x i al la fracción del objeto o i que colocamos en la mochila. f Función objetivo: El valor de los objetos introducidos en la mochila Σ i x i v i , que hay que maximizar. f Dominio o conjunto de candidatos: La colección de objetos C que queremos introducir en la mochila. f Función solución: S={x 1 ,…,x k } es solución si Σ i x i p i = p max . f Función factible: S={x 1 ,…,x k } es factible si Σ i x i p i ≤ p max . f Función selección: Existen varias estrategias posibles – Seleccionar los objetos en orden decreciente de valor. – Seleccionar los objetos en orden creciente de peso. – Seleccionar los objetos por orden decreciente de relación valor/peso. (Sólo esta conduce a la solución óptima). 40 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Problema de la Mochila Fraccionada Algoritmo Voraz Problema de la Mochila Fraccionada Algoritmo Voraz f El siguiente algoritmo implementado en C++ utiliza la técnica voraz para resolver el problema de la mochila fraccionada: float mochila(float v[n],float p[n],float x[n],float pmax){ struct objeto{ float coste; int posicion; }; objeto vp[n]; for (int i=0;i<n;i++){ vp[i].coste=v[i]/p[i]; vp[i].posicion=i; x[i]=0;} ordenar_decreciente_coste(vp) float peso=0, valor=0; int i=0, j; 41 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Problema de la Mochila Fraccionada Algoritmo Voraz (continuación) Problema de la Mochila Fraccionada Algoritmo Voraz (continuación) while (peso<=pmax && i<n){ j=vp[i].posicion; si (peso+p[j]<=pmax){ x[j]=1; valor+=v[j]; peso+=p[j]; } else { x[j]=(pmax-peso)/p[j]; valor+=v[j]*x[j]; peso=pmax; } i++; } return valor; } 42 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Problema de la Mochila Fraccionada Coste Temporal Problema de la Mochila Fraccionada Coste Temporal f El tamaño del problema es el número de objetos n. f El primer bucle realiza n iteraciones y el coste de su cuerpo es constante, de modo que el coste total del bucle es O(n). f El coste de la función de ordenación decreciente de los objetos por valor/peso es el coste del algoritmo de ordenación utilizado ( en el mejor caso O(n log n)). f El segundo bucle realiza, a lo sumo n iteraciones, y el coste de su cuerpo es constante, de manera que el coste total del bucle también es O(n). f Por tanto, el coste total del algoritmo es O(n) + O(n log n) + O(n) = O(n log n). 43 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Programación Dinámica Programación Dinámica f Con la técnica de divide y vencerás, la descomposición natural de un problema puede originar subproblemas que se solapen, de manera que al resolverlos independientemente se acabe resolviendo el mismo problema múltiples veces. f La técnica de PROGRAMACIÓN DINÁMICA es similar a la de divide y vencerás, sólo que los subproblemas obtenidos sólo se resuelven una vez. Esto exige guardar la solución de cada subproblema para, si se presenta de nuevo, no tener que volver a resolverlo. ¡Se sacrifica espacio para ganar tiempo de ejecución! f Es una técnica ascendente, que se inicia con la resolución de subproblemas más pequeños, y utiliza sus soluciones en la resolución de los subproblemas de niveles superiores. 44 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Ejemplo Los Números de Fibonacci Ejemplo Los Números de Fibonacci f La sucesión de Fibonacci viene dada por la expresión f El programa resultante utilizando divide y vencerás es: f En la resolución se repiten llamadas recursivas: ¹ ´ ¦ ≥ < + = . 2 si ; 2 si ) 2 ( ) 1 ( 1 ) ( n n n- f n- f n f long int fibonacci(int n){ if (n<2) return 1; else return fibonacci(n-2)+fibonacci(n-1); } f(n) f(n-1) f(n-2) f(n-2) f(n-3) f(n-3) f(n-4) 45 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Los Números de Fibonacci con Programación Dinámica Los Números de Fibonacci con Programación Dinámica f Resulta sencillo evitar la repetición de llamadas recursivas utilizando el siguiente esquema: f La siguiente función en C++ calcular la sucesión de Fibonacci hasta el térnimo n con programación dinamica: f El coste de este algoritmo es O(n) frente a O(2 n ) del anterior. f(n) f(n-1) f(n-2) f(n-3) f(n-4) … int * fibonacci(int f[n]){ // Se supone n>2 f[0]=0; f[1]=1; for (int i=2;i<n;i++) f[i]=f[i-1]+f[i-2]; return f; } 46 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Características de la Programación Dinámica Características de la Programación Dinámica f La descomposición en subproblemas suele hacerse de acuerdo al principio de optimalidad. f La estructura de datos donde se van guardando las soluciones de los subproblemas resueltos suele ser una tabla. f La mayor dificultad de esta técnica consiste en determinar el orden en que se deberá rellenar la tabla. Para resolver un subproblema, previamente deben estar almacenadas todas las soluciones necesarias. f La programación dinámica supone un compromiso entre el coste espacial y el coste temporal. Si para reducir un poco el coste temporal se requiere un excesivo coste espacial, puede no ser una buena técnica. 47 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Ejemplos de Programación Dinámica Ejemplos de Programación Dinámica f Algunos problemas conocidos que pueden resolverse mediante programación dinámica son: – El cálculo de los números de Fibonacci. – El cálculo de números combinatorios. – El problema de la mochila entera. – El problema de los caminos mínimos en grafos. – La multiplicación de una secuencia de matrices. – El problema del viajante. 48 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema de la Mochila Entera El Problema de la Mochila Entera f Ya vimos un algoritmo voraz para resolver el problema de la mochila fraccionaria, sin embargo, la técnica voraz no garantiza la obtención de la solución óptima cuando los objetos no se pueden fraccionar. f Ejemplo. Si tenemos 3 objetos con valores v 1 =18, v 2 =20 y v 3 =12, y pesos p 1 =6, p 2 =4 y p 3 =2, y el peso máximo de la mochila es pmax=10, entonces el algoritmo voraz calcula la solución (x 1 =0, x 2 =1, x 3 =1) que tiene un valor 32, mientras que la solución óptima es (x 1 =1, x 2 =1, x 3 =0) que tiene un valor 38. f Veremos cómo la programación dinámica si permite llegar a la solución óptima. 49 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema de la Mochila Entera El Problema de la Mochila Entera f Si llamamos f(n,p max ) al valor de la mochila en la solución óptima, aplicando el principio de optimalidad, se tiene f El algoritmo recursivo que implementa directamente la función anterior tiene un coste exponencial O(2 n ). f Sin embargo, el número total de subproblemas a resolver no es tan grande, ya que el primer parámetro de f puede oscilar entre 1 y n, y el segundo entre 0 y p max , en total n(p max +1). f Así pues, ¡hay subproblemas que se repiten! Podemos utilizar una tabla para almacenar los subproblemas resueltos, en la que en la posición (i,j) se almacene el valor de f(i,j). n n n n p p n p p n p p n p p n v p p n f p n f p n f v p n f ≥ > < > ≥ = < = ¦ ¦ ¹ ¦ ¦ ´ ¦ + − − − − = max max 1 max 1 max max max max 1 max y 1 si y 1 si y 1 si y 1 si ) ) , 1 ( ), , 1 ( max( ) , 1 ( 0 ) , ( 50 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema de la Mochila Entera Algoritmo Programación Dinámica El Problema de la Mochila Entera Algoritmo Programación Dinámica f La siguiente función implementada en C++ calcula la tabla de subproblemas con programación dinámica: void tabla_f(int n,int v[n],int p[n],int pmax, int f[n][pmax]){ for (int j=0;j<=pmax;j++) if (j<p[0]) f[0][j]=0; else f[0][j]=v[0]; for (int i=1;i<n;i++) for (int j=0;j<=pmax;j++) if (j<p[i]) f[i][j]=f[i-1][j]; else if (f[i-1][j]>f[i-1][j-p[i]]+v[i]) f[i][j]=f[i-1][j]; else f[i][j]=f[i-1][j-p[i]]+v[i]; return; } 51 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema de la Mochila Entera Ejemplo El Problema de la Mochila Entera Ejemplo f Supongamos que tenemos 5 objetos con pesos p[0]=5, p[1]=4, p[2]=2, p[3]=3, p[4]=4, y valores v[0]=20, v[1]=15, v[2]=10, v[3]=12, v[4]=14, entonces, si el peso máximo de la mochila es 10, la función anterior generaría la siguiente tabla: Peso 0 1 2 3 4 5 6 7 8 9 10 Objeto 0 0 0 0 0 0 20 20 20 20 20 20 Objeto 1 0 0 0 0 15 20 20 20 20 35 35 Objeto 2 0 0 10 10 15 20 25 30 30 35 35 Objeto 3 0 0 10 12 15 22 25 30 32 37 42 Objeto 4 0 0 10 12 15 22 25 30 32 37 42 Valor máximo de la mochila 52 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema de la Mochila Entera Algoritmo Programación Dinámica El Problema de la Mochila Entera Algoritmo Programación Dinámica f El valor máximo de la mochila será f[n,pmax]. f Por otro lado, los objetos incluidos en la mochila en la solución óptima pueden obtenerse mediante la función void objetos(int n,int v[n],int p[n],int pmax, int f[n][pmax],int sol[n]){ for (int i=n-1;i>0;i--) if (p[i]>pmax) sol[i]=0; else if (f[i-1][pmax-p[i]]+v[i]>f[i-1][pmax]){ sol[i]=1; pmax-=p[i]; } else sol[i]=0; if (p[0]<pmax) sol[0]=1; else sol[0]=0; return; } 53 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema de la Mochila Entera Coste Temporal El Problema de la Mochila Entera Coste Temporal f Cada componente de la tabla se calcula en un tiempo constante, luego la construcción de la tabla es O(np max ). f Por otro lado, la función que determina los objetos introducidos en la mochila a partir de la tabla, sólo tiene un bucle que recorre todos los objetos para ver si están o no dentro de la mochila. Como el cuerpo del bucle tiene un coste constante, su coste es O(n). f Así pues, el coste total es O(np max ) + O(n) = O(np max ). f Se observa que si p max es muy grande (p max ≥n) sería un coste cuadrático o superior, por lo que no sería muy eficiente. f Por último, hay que notar que si los pesos no fuesen enteros, el algoritmo no valdría. 54 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Viajante El Problema del Viajante f Un viajante tiene que recorrer n ciudades y regresar a la ciudad de partida, sin pasar dos veces por la misma ciudad. f Se supone que los caminos son unidireccionales y se conoce la distancia de cada camino. f ¿Cuál es recorrido que debe seguir para que sea lo más corto posible? ? % % % % 6 4 3 2 5 2 4 6 5 3 0 1 2 3 55 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Viajante Modelización Mediante Grafos El Problema del Viajante Modelización Mediante Grafos f Si representamos cada ciudad mediante un nodo de un grafo y cada camino como una arista dirigida, tendremos un grafo dirigido G=(V,A) donde – V={1,…,n} es el conjunto de vértices (ciudades). – A es el conjunto de aristas (i,j) con i,j ∈ V (caminos). – D(i,j) es la longitud de (i,j) si (i,j) ∈ A, o ∞ si (i,j) ∉ A. f Se trata de un problema de búsqueda en un grafo del ciclo Hamiltoniano de longitud mínima. ∞ 4 5 2 3 6 ∞ 6 2 2 ∞ 4 ∞ 5 1 3 ∞ 3 ∞ 0 3 2 1 0 De\A 56 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Viajante El Problema del Viajante f Suponiendo que la ciudad de partida es la 0, si llamamos C(i,W) a la longitud del camino mínimo de i a 0 que pasa por todos los vértices de W ⊂ V, siendo 0∉W, entonces f Como la ciudad de partida es la 0, la solución al problema será C(0,V-{0}). f Se cumple el principio de optimalidad. ¦ ¹ ¦ ´ ¦ ∅ ≠ − + ∅ = = ∈ W j W j C j i D W i D W i C W j si })) { , ( ) , ( ( min si ) 0 , ( ) , ( 57 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Viajante Algoritmo Recursivo (Fuerza Bruta) El Problema del Viajante Algoritmo Recursivo (Fuerza Bruta) f La siguiente función en C++ implementa la función anterior de manera recursiva float c(int n,int i,const conjunto &w,float **d){ conjunto cw(w); cw.eliminar(i); if (cw.vacio()) return d[i][0]; float dist,dmin=DMAX; for(int j=0;j<n;j++) if (cw.pertenece(j)){ dist=d[i][j]+c(n,j,cw,d); if (dist<dmin) dmin=dist; } return dmin; } 58 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Viajante Algoritmo Recursivo (Fuerza Bruta) El Problema del Viajante Algoritmo Recursivo (Fuerza Bruta) f Siendo la interfaz de la clase conjunto la siguiente f Y DMAX una constante con un valor alto para simular ∞. class conjunto{ private: int numel; // Cardinal del conjunto universal bool elementos[]; // Vector de pertenencia a conjunto public: conjunto(int n); // Inicializa al conjunto vacio conjunto(const conjunto &a); // Constructor de copia void aniadir(int i);// Añade el elemento i void eliminar(int i);// Elimina el elemento i bool pertenece(int i); // Pertenencia de i int cardinal(); // Cardinal del conjunto bool vacio(); // Comprueba si el conjunto está vacio }; 59 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Viajante Coste del Algoritmo de Fuerza Bruta El Problema del Viajante Coste del Algoritmo de Fuerza Bruta f Esta función estudia todos los posibles caminos por fuerza bruta f El coste es O(n!). 0 1 3 2 0 0 2 3 15 ∞ 6 2 4 2 4 ∞ 2 3 1 0 0 1 3 ∞ ∞ ∞ 2 5 5 6 6 3 2 1 0 0 1 2 14 18 4 2 6 5 5 4 3 ∞ 3 Origen 1ª visita 2ª visita 3ª visita Destino Distancia R e c o r r i d o Min 60 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Viajante Mejora de la eficiencia El Problema del Viajante Mejora de la eficiencia f La función anterior repite llamadas para calcular C(i,W), y por tanto es ineficiente. f Utilizando la técnica de programación dinámica, podemos guardar los valores de C(i,W) en una tabla para evitar repetir su cálculo. f Para indexar la tabla en la segunda dimensión, asignaremos un código único a cada subconjunto W para identificarlo. f Puesto que el número de ciudades es n, el número de posibles subconjuntos es 2 n . Si representamos cada subconjunto mediante un vector entonces, id(W) = x 0 2 0 + x 1 2 1 +…+ x n-1 2 n-1 asigna un código único a cada subconjunto. ¹ ´ ¦ ∈ ∉ = = − w i w i x x x W i n si 1 si 0 con ] ,..., [ 1 0 61 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Viajante Algoritmo Programación Dinámica El Problema del Viajante Algoritmo Programación Dinámica f La siguiente función en C++ calcula la distancia mínima utilizando programación dinámica float dist_min(int n,int i,const conjunto &w, float **d,float **c){ conjunto cw(w); cw.eliminar(i); if (cw.vacio()) return d[i][0]; if (c[i][cw.id()]>0) return c[i][cw.id()]; float dist,dmin=DMAX; for(int j=0;j<n;j++) if (cw.pertenece(j)){ dist=d[i][j]+dist_min(n,j,cw,d,c); if (dist<dmin) dmin=dist; } c[i][cw.id()]=dmin; return dmin; } 62 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Viajante Construcción de la Tabla El Problema del Viajante Construcción de la Tabla f Para el ejemplo anterior, con D(i,j) la tabla de distancias mínimas resultante C(i,W) es ∞ 4 5 2 3 6 ∞ 6 2 2 ∞ 4 ∞ 5 1 3 ∞ 3 ∞ 0 3 2 1 0 De\A i \ W 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 14 -1 1 -1 -1 -1 -1 6 -1 -1 -1 ∞ -1 -1 -1 12 -1 -1 -1 2 -1 -1 11 -1 -1 -1 -1 -1 8 -1 16 -1 -1 -1 -1 -1 3 -1 -1 10 -1 6 -1 11 -1 -1 -1 -1 -1 -1 -1 -1 -1 63 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Viajante Coste de la Programación Dinámica El Problema del Viajante Coste de la Programación Dinámica f El coste del algoritmo para completar la tabla C(i,W) supone: – Cálculo de C(i,∅) para i=1,…,n-1: n-1 consultas a tabla; – Cálculo de C(i,W) para 1≤ k = Cardinal(W) ≤ n-2: – Cálculo C(0,V): n-1 sumas. f En total que, aunque es exponencial, es de orden menor que O(n!). f El precio de esta mejora es el coste espacial de la tabla C(i,W) que es Θ(n2 n ). sumas; k k n n | . | \ | − − 2 ) 1 ( ) 2 ( 2 ) 1 ( ) 1 ( 2 2 2 1 n n k n k n k n n Θ = | . | \ | | . | \ | − − + − Θ ∑ − = 64 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Viajante Itinerario del Recorrido Mínimo El Problema del Viajante Itinerario del Recorrido Mínimo f El itinerario del recorrido mínimo podemos calcularlo a partir de la tabla C(i,W) mediante la siguiente función void camino_min(int n,conjunto &w,float **d, float **c,int *camino){ float dist,dmin; int jmin; for (int i=1;i<n;i++){ dmin=DMAX; for(int j=0;j<n;j++) if (w.pertenece(j)){ w.eliminar(j); dist=d[camino[i-1]][j]+c[j][w.id()]; w.aniadir(j); if (dist<dmin) {dmin=dist; jmin=j;} } camino[i]=jmin; w.eliminar(jmin)} return;} 65 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Viajante Itinerario del Recorrido Mínimo El Problema del Viajante Itinerario del Recorrido Mínimo f Para el ejemplo, la función anterior devuelve el itinerario camino[4]={0,1,2,3}, es decir ¡Que efectivamente es el camino de mínima longitud! % % % % 6 4 3 2 5 2 4 6 5 3 0 1 2 3 66 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Vuelta Atrás (Backtracking) Vuelta Atrás (Backtracking) f La técnica de VUELTA ATRÁS es parecida a la técnica devoradora en que resuelve un problema de manera gradual tomando decisiones o realizando acciones que permiten ir construyendo progresivamente la solución del problema. Sin embargo, a diferencia de la técnica devoradora, las decisiones tomadas pueden volverse a reconsiderar si no se llega una solución aceptable u óptima. f Básicamente, esta técnica comienza a resolver el problema buscando todas las soluciones posibles, como lo hace la técnica de fuerza bruta, pero cuando detecta que una solución parcial no puede llegar a ser óptima, la rechaza y no construye más soluciones a partir de ella. f Se suele aplicar también a problemas de decisión u optimización con o sin restricciones, cuando no existe un criterio óptimo de toma de decisiones locales. 67 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Recordemos los Elementos de un Problema de Decisión Recordemos los Elementos de un Problema de Decisión f FUNCIÓN OBJETIVO, que hay que minimizar o maximizar y depende de las variables x 1 , …, x n . f DOMINIO, que contiene el conjunto de valores que pueden tomar las variables. f FUNCIÓN SOLUCIÓN, que permite saber si unos determinados valores de las variables son o no solución del problema. f DECISIÓN, que es la asignación de un valor a una variable. f RESTRICCIONES, que son las condiciones que deben cumplir las soluciones. f FUNCIÓN FACTIBLE, que permite saber si una solución cumple o no las restricciones. f SOLUCIÓN EN CURSO, que es el conjunto de decisiones factibles tomadas hasta el momento. 68 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Espacio de Búsqueda Espacio de Búsqueda f La solución del problema puede expresarse como una tupla (x 1 ,…,x n ) con x i ∈C i , siendo C i el dominio de x i . f Así pues, el número total de posibles soluciones es siendo k i el número de elementos de C i . f El conjunto de todas las posibles soluciones se conoce como ESPACIO DE BÚSQUEDA y se representa como un árbol ∏ = n i i k 1 x 11 x 21 1ª decisión 2ª decisión nª decisión R e c o r r i d o e n p r o f u n d i d a d x 22 x 2k2 … x n1 x n2 x nkn … x 12 x 21 x 22 x 2k2 … x 21 x 22 x 2k2 … x 1k1 ... x n1 x n2 x nkn … ... ... ... … … ... 69 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Recorrido en Profundidad del Espacio de Búsqueda Recorrido en Profundidad del Espacio de Búsqueda f Las soluciones se construyen partiendo de la raíz del espacio de búsqueda y tomando decisiones realizando un recorrido en profundidad del mismo. f Se dice una solución en curso es COMPLETABLE si a partir de ella se puede alcanzar la solución del problema. f Mientras que la técnica de fuerza bruta recorrería todo el espacio de búsqueda, lo que supondría un coste exponencial, la técnica de vuelta atrás, cuando llega a una solución no completable, da marcha atrás y busca por otro camino del espacio de búsqueda, evitando así recorrer todo el subárbol que queda por debajo de dicha solución no completable. 70 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Diferentes Versiones de Algoritmos con Vuelta Atrás Diferentes Versiones de Algoritmos con Vuelta Atrás f Dependiendo del tipo de problema que tengamos que resolver existen diferentes formas de aplicar la vuelta atrás: – Vuelta atrás para una solución: El algoritmo recorre el espacio de búsqueda hasta que encuentra la primera solución. Es el más sencillo. – Vuelta atrás para todas las soluciones: El algoritmo recorre el espacio de búsqueda guardando todas las soluciones que encuentra hasta que ya no haya más. – Vuelta atrás para la mejor solución: El algoritmo recorre el espacio de búsqueda comparando cada solución que encuentra con la mejor solución obtenida hasta el momento, y quedándose con la mejor. Cuando ya no hay más soluciones, devuelve la mejor encontrada. Suele aplicarse a problemas de optimización. 71 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Algorítmo Genérico con Vuelta Atrás para Todas las Soluciones Algorítmo Genérico con Vuelta Atrás para Todas las Soluciones f El siguiente algoritmo calcula todas las soluciones de un problema con la técnica de vuelta atrás funcion vuelta_atras(entero i,sol[n]) para_todo x en Ci sol[i]=x si completable(sol,i) entonces si solucion(sol) entonces guardar(sol) fin_si si (i<k) entonces vuelta_atras(i+1,sol) fin_si fin_si fin_para_todo fin_funcion 72 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Poda del Espacio de Búsqueda Poda del Espacio de Búsqueda f El algoritmo anterior no genera el espacio de búsqueda de forma explícita, sino implícitamente mediante llamadas recursivas. f Cuando se llega a una solución en curso no completable, no se realizan más llamadas recursivas y por tanto no se construye el subárbol correspondiente del espacio de búsqueda. Esto se conoce como PODA del espacio de búsqueda. f La poda es un mecanismo que permite descartar del recorrido ciertas zonas del espacio de búsqueda, bien porque allí no haya soluciones, bien porque ninguna de las soluciones de esas zonas es la óptima. f Cuanto mayor sea el número de nodos podados, más eficiente será la búsqueda de soluciones. 73 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Coste Temporal del Algoritmo de Vuelta Atrás Coste Temporal del Algoritmo de Vuelta Atrás f El coste temporal de un algoritmo que utilice la técnica de vuelta atrás, suele depender de: – El número de nodos del espacio de búsqueda que se visitan v(n). – El coste de las funciones solucion y completable en cada nodo p(n). En total O(p(n)v(n)). f Si se consigue que la función completable pode muchos nodos, el tamaño de v(n) se puede reducir drásticamente: – Si se reduce a un solo nodo (solución voraz) tendremos un coste O(p(n)n). – Por el contrario, en el peor de los casos (solución por fuerza bruta), tendremos un coste O(p(n)k n ). 74 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Ejemplos de Vuelta Atrás Ejemplos de Vuelta Atrás f Algunos problemas conocidos que pueden resolverse mediante la técnica de vuelta atrás: – El problema de las ocho reinas. – La suma de subconjuntos. – El problema de la mochila entera. – El problema del coloreado de grafos. – La búsqueda de ciclos Hamiltonianos en grafos. – El recorrido de un laberinto. 75 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Coloreado de Grafos El Problema del Coloreado de Grafos f Se dispone de un grafo G y de k colores. ¿Es posible colorear los vértices de G de manera que no haya dos vértices adyacentes con el mismo color? f Otro famoso problema reducible a este es el del coloredado de mapas. ¿Puede pintarse un mapa de manera que no haya dos regiones adyacentes con el mismo color, utilizando k colores? f Cada región se representa como un vértice del grafo y si dos regiones son adyacentes, sus respectivos vértices de conectan con una arista. 76 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Coloreado de Grafos Espacio de Búsqueda Coloreado de Grafos Espacio de Búsqueda f Si el grafo tiene n vértices, representaremos el color de cada vértice en un vector color[n]={c 1 ,…,c n } siendo, c i ∈{1,…,k} el color del vértice i. f El espacio de búsqueda asociado tiene k n posibles soluciones. Por ejemplo, para n=3 y k=3 se tiene el siguiente espacio de búsqueda con 27 posibles soluciones. 1 1 er vértice 2º vértice 3 er vértice 1 2 1 3 2 2 1 3 3 2 1 3 2 1 2 1 3 2 2 1 3 3 2 1 3 3 1 2 1 3 2 2 1 3 3 2 1 3 77 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Coloreado de Grafos Algoritmo con Vuelta Atrás Coloreado de Grafos Algoritmo con Vuelta Atrás f La siguiente función en C++ resuelve el problema del coloreado de grafos con la técnica de vuelta atrás: void colorea(int n,int k,bool **ady,int color[],int i){ for (int j=0;j<k;j++){ color[i]=j; if (completable(ady,color,i)) if (i==n-1) imprimir(n,color); else colorea(n,k,ady,color,i+1); } return; } bool completable(bool **adj,int color[],int i){ int j=0; for (int j=0;j<i;j++) if (adj[j][i] && color[j]==color[i]) return false; return true;} 78 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Coloreado de Grafos Ejemplo Coloreado de Grafos Ejemplo f Aplicando la función anterior al mapa de 5 regiones con 3 colores 0, 1 y 2, se obtienen seis posibles soluciones 0 1 4 3 2 | | | | | . | \ | = 0 1 1 1 0 1 0 1 0 1 1 1 0 1 1 1 0 1 0 1 0 1 1 1 0 Ady 79 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Algoritmo Genérico con Vuelta Atrás para la Mejor Solución Algoritmo Genérico con Vuelta Atrás para la Mejor Solución f El siguiente algoritmo calcula la mejor solución de un problema de optimización con la técnica de vuelta atrás funcion vuelta_atras(entero i,sol[n]) para_todo x en Ci hacer sol[i]=x si completable(sol,i)∧coste(sol,i)<mcoste entonces si solucion(sol) entonces mejor_solucion=sol mcoste=coste(sol,i) fin_si si (i<k) entonces vuelta_atras(i+1,sol) fin_si fin_si fin_para_todo fin_funcion 80 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Poda Basada en la Mejor Solución Poda Basada en la Mejor Solución f Cuando estemos ante un problema de optimización, el objetivo del algoritmo es recorrer el espacio de búsqueda para encontrar la mejor solución. f En este caso, además de la poda que realiza la función completable, podemos realizar otra poda basada en el coste de la mejor solución en curso. f La idea es que una solución en curso se poda, cuando, a pesar de ser completable, no es posible conseguir una solución mejor que la mejor solución en curso, aún cuando se genere todo el subárbol del espacio de búqueda que cuelga de ella (coste(sol,i)≥mejorcoste). f Como antes, esta poda es más efectiva cuantos más nodos consiga eliminar. 81 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema de la Mochila Entera Espacio de Búsqueda El Problema de la Mochila Entera Espacio de Búsqueda f El problema de la mochila entera también puede resolverse mediante la técnica de vuelta atrás. f Si tenemos n objetos y representamos el contenido de la mochila con un vector sol[n]={x 1 ,…,x n } donde entonces el espacio de búsqueda asociado es ¹ ´ ¦ = mochila la en está objeto el si 1 mochila la en está no objeto el si 0 i i x i Objeto 1 Objeto 2 Objeto n 0 1 0 1 ... ... ... … 0 1 0 1 0 1 82 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema de la Mochila Entera Poda Basada en la Mejor Solución El Problema de la Mochila Entera Poda Basada en la Mejor Solución f Puesto que se trata de un problema de optimización, aplicaremos también la poda basada en la mejor solución en curso. f Para ello resulta útil disponer de una función cota(sol,i) que calcule una cota superior del valor de la mochila en las soluciones que pueden obtenerse a partir de la solución en curso. f Con esta función, cada vez que cota(sol,i) < maxval, siendo maxval el máximo valor de la mochila encontrado hasta el momento (al comienzo maxval=0), podaremos el subárbol del espacio de búsqueda que cuelgue de dicha solución en curso y haremos la vuelta atrás. f En nuestro caso, esta cota puede obtenerse mediante el algoritmo voraz para el problema de la mochila fraccionada. 83 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema de la Mochila Entera Algoritmo con Vuelta Atrás El Problema de la Mochila Entera Algoritmo con Vuelta Atrás f La siguiente función en C++ resuelve el problema de la mochila entera con la técnica de vuelta atrás: void llenar(int sol[],float valact,float pesoact, int mejorsol[],float &maxval,int i){ float tval,tpeso; for (int j=1;j>=0;j--){ sol[i]=j; tval=valact+j*valor[i]; tpeso=pesoact-j*peso[i]; if (tpeso>=0 && maxval<cotaval(tempval,tpeso,i+1)){ if (i<n-1) fillup(sol,tval,tpeso,mejorsol,maxval,i+1); else {maxvalue=tempvalue; copiar(sol,bestsol);} } } } 84 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema de la Mochila Entera Función Cota del Valor Máximo El Problema de la Mochila Entera Función Cota del Valor Máximo f Siendo n (número de objetos) valor[] (valor de los objetos) y peso[] (peso de los objetos) variables globales, y siendo la función cotaval la siguiente: f Para que esta función trabaje correctamente, es necesario que los objetos estén ordenados de manera decreciente por valor/peso. f Además, no es necesario que los pesos sean enteros como ocurría con la programación dinámica. float cotaval(float val,float pesomax,int i){ for (int j=i;j<n;j++) if (peso[j]>pesomax) return val+pesomax/peso[j]*valor[j]; else {val+=valor[j]; pesomax-=peso[j];} return val; } 85 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema de la Mochila Entera Ejemplo El Problema de la Mochila Entera Ejemplo f Si hay 5 objetos de valores valor[]={30,40,60,10,20} y pesos peso[]={6,10,20,5,15}, entonces la función realiza el siguiente recorrido del espacio de búsqueda 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 Objeto 0 Objeto 1 Objeto 3 1 1 1 0 1 0 1 0 0 1 0 1 0 1 0 0 1 1 0 1 0 1 0 0 1 0 1 0 1 0 Objeto 2 Objeto 4 p=6,v=30 p=10,v=40 p=20,v=60 p=5,v=10 p=15,v=20 maxval 80 90 100 86 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Mejoras de la Vuelta Atrás Mejoras de la Vuelta Atrás f Una posible mejora sería comenzar buscando una solución factible por algún procedimiento rápido, e inicializar el valor de la mejor solución con el valor de dicha solución. De esta manera, la poda basada en mejor solución comenzaría a funcionar antes. f Otra posible mejora consiste en guardar para cada variable x i la lista de los valores de su dominio C i : – Cada vez que se asigna un valor a una variable, se eliminan de todas las variables no asignadas los valores incompatibles con la asignación. Esto reduce el espacio de búsqueda aunque también supone un coste. – Si a alguna variable no le quedan valores posibles, entonces no es posible la solución y se hace vuelta atrás. f También está la posibilidad de considerar árboles con recorrido dinámico para los espacios de búsqueda. 87 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Ramificación y Poda (Branch & Bound) Ramificación y Poda (Branch & Bound) f La técnica de RAMIFICACIÓN Y PODA es básicamente una mejora de la técnica de vuelta atrás que se basa en un recorrido dinámico más eficiente del espacio de búsqueda. f La principal diferencia es que, mientras que la técnica de vuelta atrás realiza un recorrido ciego del espacio de búsqueda, la técnica de ramificación y poda realiza un recorrido informado. f En un recorrido ciego, bien sea en profundidad o en anchura, se conoce perfectamente el siguiente nodo a visitar (siguiente hijo o hermano del nodo actual), es decir, el orden del recorrido está establecido a priori, mientras que en un recorrido informado, se busca el siguiente nodo más prometedor en base a la información de que se disponga. f Se aplica fundamentalmente a problemas de optimización con restricciones, donde el espacio de búsqueda es un árbol. 88 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Nodos Prometedores Nodos Prometedores f Diremos que un nodo del espacio de búsqueda es PROMETEDOR, si la información que tenemos de ese nodo en el recorrido indica que expandiéndolo se puede conseguir una solución mejor que la mejor solución obtenida hasta el momento. f Esta información puede provenir de la evaluación del camino ya recorrido hasta el nodo, o bien de la evaluación de los caminos quedan por recorrer desde el nodo a posibles soluciones. f Para guiar la búsqueda es fundamental disponer de una función que, no sólo diga si un nodo es o no prometedor, sino que además estime lo prometedor que es. 89 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Caracterización del Espacio de Búsqueda en Ramificación y Poda Caracterización del Espacio de Búsqueda en Ramificación y Poda f El espacio de búsqueda se representa mediante un árbol en el que hay tres clases de nodos: – Nodo vivo: Es un nodo hasta el cual la solución en curso es factible, prometedora, y del que no se han generado aún todos sus hijos (no se ha completado la solución). Indican caminos abiertos de búsqueda. – Nodo muerto: Es un nodo del que no van a generarse más hijos porque, o bien ya se han generado todos sus hijos, o bien no es factible, o bien no es prometedor. Indican caminos cerrados de búsqueda. – Nodo en expansión: Es aquel del que se están generando sus hijos en un determinado instante. f En un determinado instante de la búsqueda puede haber muchos nodos vivos y muertos pero sólo uno en expansión. 90 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Funcionamiento de la Búsqueda en Ramificación y Poda Funcionamiento de la Búsqueda en Ramificación y Poda f En la técnica de vuelta atrás, cada vez que se generaba un hijo del nodo en expansión, este pasaba inmediatamente a ser el nodo en expansión. En consecuencia, los únicos nodos vivos son los que están en el camino de la raíz al nodo en expansión. f Por el contrario, en la técnica de ramificación y poda se generan todos los hijos del nodo en expansión antes de que cualquier otro nodo vivo pase a ser el nuevo nodo en expansión. Esto supone que puede haber nodos vivos en distintos caminos y tendremos que guardar la lista de nodos vivos en alguna estructura de datos (suelen ser pilas o colas). 91 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Estrategias de Búsqueda Estrategias de Búsqueda f Existen diferentes estrategias para elegir el siguiente nodo en expansión de la lista de nodos vivos: – Más antiguo. Se elige como nodo en expansión el más antiguo de la lista. En este caso la lista se implementa como una cola. Produce un recorrido en anchura. – Menos antiguo. Se elige como nodo en expansión el menos antiguo de la lista. En este caso la lista se implementa como una pila. Produce un recorrido en profundidad. – Más prometedor. Se elige como nodo de expansión el nodo más prometedor de la lista. La lista se implementa como una cola con prioridades, donde la prioridad de un nodo es lo prometedor que es. Produce un recorrido informado y ¡es el que suele utilizar la técnica de ramificación y poda! 92 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Algoritmo Genérico con Ramificación y Poda Algoritmo Genérico con Ramificación y Poda f El siguiente algoritmo calcula la mejor solución de un problema de optimización con ramificación y poda: funcion ramificacion_y_poda() inicializar(C); insertar(C,<sol,0,prioridad(sol,0)>) mientras no_vacia(C) hacer <sol,k>=primero(C) para_todo x en C k hacer sol[k]=x si completable(sol,k)∧coste(sol,k)<mcoste entonces si solucion(sol) entonces mejor_solucion=sol; mcoste=coste(sol,k) si_no insertar(C,<sol,k+1,prioridad(sol,k+1)>) fin_si fin_para_todo fin_mientras devolver (mejor_solucion, mcoste) 93 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Algoritmo Genérico con Ramificación y Poda Algoritmo Genérico con Ramificación y Poda f En el algoritmo anterior se tiene que: – C es una cola con prioridades, es decir, sus elementos están ordenados de acuerdo a sus prioridades. – prioridad(sol,k) es una función que devuelve la prioridad de un nodo vivo, que será lo prometedor que es. Esta función sirve para dirigir la búsqueda y llegar pronto a la solución óptima. – La condición completable(sol,k) sirve para realizar una poda cuando la solución no es factible, mientras que la condición coste(sol,k)<mcoste sirve para realizar una poda basada en la mejor solución. En ambos casos el nodo correspondiente se convierte en un nodo muerto. f Habitualmente la función de prioridad también suele ser la función de coste. 94 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Ramificación y Poda Coste Temporal Ramificación y Poda Coste Temporal f El coste temporal de un algoritmo que utilice la técnica de ramificación y poda, depende fundamentalmente de: – El número de nodos del espacio de búsqueda que se visitan v(n). Que depende a su vez de lo buenas que sean las funciones completable y coste para hacer podas, y lo buena que sea la función prioridad para dirigir la búsqueda. – El coste de las funciones prioridad, completable y coste en cada nodo p(n). – El coste del mantenimiento de la cola con prioridad. En total O(p(n)v(n)), donde v(n) suele ser d n con d= max |C k |. 95 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema de la Mochila Entera Mejora con Ramificación y Poda El Problema de la Mochila Entera Mejora con Ramificación y Poda f Se puede mejorar la solución dada al problema de la mochila entera con la técnica de vuelta atrás, si, además de las podas que realizaba esta, se dirige la búsqueda con la función de prioridad. f Tomando como función de prioridad la misma función que la que calculaba una cota superior del valor de la mochila para cada nodo del espacio de búsqueda, se expandirán primero los nodos que tengan una cota mayor, y que probablemente llevarán a una solución con un valor de la mochila mayor. 96 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema de la Mochila Entera Estructura de un Nodo El Problema de la Mochila Entera Estructura de un Nodo f Cada nodo del espacio de búsqueda debe contener la información necesaria para generar sus hijos o para decidir si es solución o no puede serlo. f Utilizaremos la siguiente estructura de datos en C++: Los valores de pes y val podrían calcularse sin necesidad de almacenarlos pero es más eficiente pasarlos a los hijos. Esto se conoce como MARCAJE. class nodo{ private: int numel; // Número total de objetos int *sol; // Vector solución int actual; // Objeto hasta el que se han tomado decisiones float pes; // Peso restante de la mochila float val; // Valor de la mochila float prio; // Prioridad del nodo } 97 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema de la Mochila Entera Función de Coste y Prioridad El Problema de la Mochila Entera Función de Coste y Prioridad f La siguiente función en C++ se utiliza como función de coste parar la poda y también como función de prioridad para dirigir la búsqueda float cotavalor(nodo *x){ nodo *sig; int i=x->etapa(); if (i>=x->numobjetos() || x->peso()==0) return x->valor(); else if (weight[i]>x->peso()) return x->valor()+x->peso()/weight[i]*value[i]; else { sig=new nodo(*x); sig->suma_valor(value[i]); sig->resta_peso(weight[i]); sig->sig_etapa(); return val_bound(sig); } 98 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema de la Mochila Entera Algoritmo con Ramificación y Poda El Problema de la Mochila Entera Algoritmo con Ramificación y Poda f La siguiente función en C++ resuelve el problema de la mochila entera con la técnica de ramificación y poda: nodo* rellenar(int n,float pesomax){ priority_queue<nodo*,vector<nodo*>,mayor_pri> c; nodo raiz(n,pesomax); nodo *hijo,*nodoexp,*solucion; c.push(&raiz); solucion=&raiz; while (!c.empty()){ nodoexp=c.top(); c.pop(); if (nodoexp->prioridad()>=solucion->valor()) for (int j=1;j>=0;j--){ hijo=new nodo(*nodoexp); hijo->fija_objeto(j); hijo->suma_valor(j*value[hijo->etapa()]); hijo->resta_peso(j*weight[hijo->etapa()]); hijo->sig_etapa(); 99 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema de la Mochila Entera Algoritmo con Ramificación y Poda El Problema de la Mochila Entera Algoritmo con Ramificación y Poda f Siendo c una cola con prioridad implementada en la librería de plantillas estándar de C++, con el orden f Y siendo valor[] (valor de los objetos) y peso[] (peso de los objetos) variables globales. hijo->fija_prio(cotaval(hijo)); if (hijo->peso()>=0 && solucion->valor()<hijo->prioridad()) if (hijo->solucion()) solucion=hijo; else c.push(hijo); } delete nodoexp; } return solucion; } bool mayor_pri::operator()(nodo *a,nodo *b){ return (a->prioridad()<b->prioridad()); } 100 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema de la Mochila Entera Ejemplo El Problema de la Mochila Entera Ejemplo f Si hay 5 objetos de valores valor[]={30,40,60,10,20} y pesos peso[]={6,10,20,5,15}, entonces la función realiza el siguiente recorrido del espacio de búsqueda 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 Objeto 0 Objeto 1 Objeto 3 1 1 1 0 1 0 1 0 0 1 0 1 0 1 0 0 1 1 0 1 0 1 0 0 1 0 1 0 1 0 Objeto 2 Objeto 4 p=6,v=30 p=10,v=40 p=20,v=60 p=5,v=10 p=15,v=20 maxval 100 c=112 c=112 c=118 c=92 c=98 c=100 c=100 c=76,67 c=100 c=70 c=103 c=100 120 100 101 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Viajante El Problema del Viajante f Recordemos que se trata de buscar el camino más corto para visitar n ciudades y volver a la ciudad de partida sin pasar dos veces por la misma ciudad. f Ya vimos una solución con programación dinámica, pero requería un coste en memoria excesivo. ? % % % % 6 4 3 2 5 2 4 6 5 3 0 1 2 3 102 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Viajante Modelización Mediante Grafos El Problema del Viajante Modelización Mediante Grafos f Como ya vimos, modelizaremos el problema con un grafo G=(V,A) donde – V={1,…,n} es el conjunto de vértices (ciudades). – A es el conjunto de aristas (i,j) con i,j ∈ V (caminos). – D(i,j) es la longitud de (i,j) si (i,j) ∈ A, o ∞ si (i,j) ∉ A. f Representaremos la solución con un vector sol[n] que almacenará en la posición i, la ciudad a visitar en i-esimo lugar. ∞ 4 5 2 3 6 ∞ 6 2 2 ∞ 4 ∞ 5 1 3 ∞ 3 ∞ 0 3 2 1 0 De\A 103 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Viajante Espacio de Búsqueda El Problema del Viajante Espacio de Búsqueda f El conjunto de posibles soluciones serán todas las permutaciones de las n-1 ciudades que hay que visitar, es decir (n-1)! f Si por ejemplo, tenemos 4 ciudades, el árbol del espacio de búsqueda tendrá 3!= 6 ramas, que serán 0 1 3 2 0 0 2 3 2 3 1 0 0 1 3 3 2 1 0 0 1 2 Origen 1ª visita 2ª visita 3ª visita Destino R e c o r r i d o 104 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Determinación de una Cota del Coste para una Solución en Curso Determinación de una Cota del Coste para una Solución en Curso f Para que la poda basada en la mejor solución sea efectiva, conviene utilizar un buena función que acote inferiormente el coste del camino mínimo para una solución en curso. f Una cota muy sencilla podría ser la suma de los caminos ya elegidos. f Parece lógico también utilizar esta cota como prioridad, ya que, en principio, los caminos más cortos recorridos hasta el momento, parecen los más prometedores y deben intentarse en primer lugar. 105 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Viajante Algoritmo con Ramificación y Poda El Problema del Viajante Algoritmo con Ramificación y Poda f La siguiente función en C++ resuelve el problema del viajante con al técnica de ramificación y poda: nodo* camino_minimo(int n){ priority_queue<node*,vector<node*>,menor_pri> c; nodo raiz(n); raiz.sig_etapa(); c.push(&raiz); nodo *hijo,*nodoexp,*sol; sol=new nodo(n); sol->fija_distancia(DMAX); while (!c.empty()){ nodoexp=c.top(); c.pop(); 106 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Viajante Algoritmo con Ramificación y Poda El Problema del Viajante Algoritmo con Ramificación y Poda if (nodoexp->distance()<sol->distance()) for (int j=1;j<n;j++) if (!nodoexp->visitado(j)){ hijo=new nodo(*nodoexp); hijo->visita(j); if (hijo->distancia()<sol->distancia()) if (hijo->solucion()){ hijo->visita(0); if (hijo->distancia()<sol->distancia()) sol=hijo; } else c.push(child); else delete hijo; } delete nodoexp; } return sol;} 107 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Viajante Algoritmo con Ramificación y Poda El Problema del Viajante Algoritmo con Ramificación y Poda f Siendo la interfaz de la clase nodo la siguiente class nodo{ int numel; // Número de ciudades int *camino; // Ciudades visitadas hasta el momento int actual; // Etapa actual del viaje float dist; // Distancia recorrida hasta el momento public: nodo(int n); // Constructor por defecto nodo(const nodo &a); // Constructor de copia. void visita(int x); // Vista la ciudad x void sig_etapa(); // Pasa a la siguiente etapa void fija_distancia(float a); // Fija la distancia float distancia(); // Devuelve la distancia recorrida bool visitado(int x); //Comprueba si se ha visitado x bool solucion(); // Mira si ha terminado el viaje }; 108 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Viajante Ejemplo con Ramificación y Poda El Problema del Viajante Ejemplo con Ramificación y Poda f Para nuestro ejemplo, esta función realizaría el siguiente recorrido del espacio de búsqueda: 15 14 18 0 1 3 2 0 0 2 3 2 3 1 0 0 1 3 3 2 1 0 0 1 2 Origen 1ª visita 2ª visita 3ª visita Destino Distancia R e c o r r i d o Min 3 ∞ 3 7 ∞ 8 7 13 15 13 18 12 14 109 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Viajante Mejora de la Poda El Problema del Viajante Mejora de la Poda f Se puede conseguir una poda más efectiva del espacio de búsqueda si se calcula la cota inferior del coste del camino mínimo reduciendo la matriz de adyacencia. f Una matriz reducida es una matriz cuyos elementos son no negativos y además, cada fila y cada columna contiene al menos un 0. f Se puede reducir una matriz restando cantidades constantes a los elementos de sus filas o columnas. En tal caso, si se resta la misma constante c a todos los elementos de una fila o columna de la matriz de adyacencia, cualquier circuito hamiltoniano se verá reducido en dicha cantidad c. f En consecuencia, se puede utilizar la suma de constantes utilizadas en la reducción de la matriz de adyacencia como cota inferior de la distancia mínima. 110 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Reducción de la Matriz de Adyacencia (Ejemplo) Reducción de la Matriz de Adyacencia (Ejemplo) f Consideremos la matriz de adyacencia del ejemplo: f Para reducir la primera fila, tomamos como c el mínimo elemento de dicha fila y lo restamos a toda la fila f Y la cota inferior del camino mínimo es 3. | | | | . | \ | ∞ ∞ ∞ ∞ ∞ ∞ 4 5 2 6 6 2 4 5 3 3 | | | | . | \ | ∞ ∞ ∞ ∞ ∞ ∞ 4 5 2 6 6 2 4 5 3 3 | | | | . | \ | ∞ ∞ ∞ ∞ ∞ ∞ 4 5 2 6 6 2 4 5 0 0 Fila 1 c=3 111 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Reducción de la Matriz de Adyacencia (Ejemplo) Reducción de la Matriz de Adyacencia (Ejemplo) f Continuando con la reducción queda f Y la suma total de las constantes restadas es 3+4+2+2=11, que es una cota inferior del camino mínimo. f Utilizando esta cota como la prioridad de cada nodo, la poda es mucho mayor y el algoritmo más eficiente. | | | | . | \ | ∞ ∞ ∞ ∞ ∞ ∞ 4 5 2 6 6 2 4 5 0 0 | | | | . | \ | ∞ ∞ ∞ ∞ ∞ ∞ 4 5 2 6 6 2 0 1 0 0 Fila 2 c=4 | | | | . | \ | ∞ ∞ ∞ ∞ ∞ ∞ 4 5 2 4 4 0 0 1 0 0 Fila 3 c=2 | | | | . | \ | ∞ ∞ ∞ ∞ ∞ ∞ 4 5 2 4 4 0 0 1 0 0 | | | | . | \ | ∞ ∞ ∞ ∞ ∞ ∞ 2 3 0 4 4 0 0 1 0 0 Fila 4 c=2 ¡Matriz Reducida! 112 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Cálculo de la Cota Inferior para los Nodos del Espacio de Búsqueda Cálculo de la Cota Inferior para los Nodos del Espacio de Búsqueda f Veamos cómo calcular la cota inferior para los nodos distintos del raíz. f Supongamos que A es la matriz de adyacencia reducida para un nodo x, y sea y el nodo hijo de x que se obtiene al incluir la arista (i,j) en el recorrido. Entonces para calcular la cota inferior del nodo y, hay que seguir los pasos siguientes: – Cambiar todos los elementos de la fila i y la columna j de A por ∞. – Reducir la matriz A con excepción de la fija i y columna j. – La cota del nodo y es la suma de la cota del nodo x, A(i,j) y las constantes restadas al reducir A. 113 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s Cálculo de la Cota Inferior Ejemplo Cálculo de la Cota Inferior Ejemplo f Siguiendo con el ejemplo: | | | | . | \ | ∞ ∞ ∞ ∞ ∞ ∞ 2 3 0 4 4 0 0 1 0 0 Cota=11 | | | | . | \ | ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ 2 0 0 0 0 1 | | | | . | \ | ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ 0 0 0 1 0 0 | | | | . | \ | ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ 2 0 0 1 0 0 1 Cota=11+0+4 Cota=11+∞+8 Cota=11+0+3 1 2 3 0 114 Metodología y Tecnología de la Programación II A n á l i s i s d e T é c n i c a s A l g o r í t m i c a s El Problema del Viajante Ejemplo con Mejora de la Poda El Problema del Viajante Ejemplo con Mejora de la Poda f Para nuestro ejemplo, con esta otra cota se realizaría el siguiente recorrido el espacio de búsqueda: 14 0 1 3 2 0 0 2 3 2 3 1 0 0 1 3 3 2 1 0 0 1 2 Origen 1ª visita 2ª visita 3ª visita Destino Distancia R e c o r r i d o Min 15 ∞ 14 14 18 14 14 11
Copyright © 2024 DOKUMEN.SITE Inc.