Vista previa parcial del texto
¡Descarga Guion 10 y más Apuntes en PDF de Física solo en Docsity!
PROGRAMACIÓN y gr Grado en Física ¿ UY) Universidad de Granada Facultad de Ciencias DEPARTAMENTO DE ARQUITECTURA Y TECNOLOGÍA DE COMPUTADORES Práctica 10 Práctica 10: problemas de optimización. El problema del viajante de comercio. Requisitos previos a la sesión de prácticas El alumno deberá haber estudiado previamente la materia correspondiente a los Temas 1al 11, haber realizado hasta la Relación 7 de problemas y haber leído atentamente este guión antes del comienzo de la sesión. Texto recomendado: Problemas de Programación, capitulos 1-9. Editorial AVICAM. 1. Introducción Existen multitud de problemas que, aún contando con un algoritmo para resolverlos de forma exacta, es imposible encontrar una solución en un tiempo razonable. Uno de estos problemas, muy conocido y estudiado, es el llamado "Problema del viajante de comercio", que se plantea de la siguiente forma: Un viajante de comercio debe recorrer N ciudades, partiendo de una de ellas y terminando en la misma ciudad de partida. ¿Cuál es el recorrido más corto, el que supone menos kilómetros? Este problema parece sencillo de resolver. Bastaría con comprobar las distintas formas de recorrer las N ciudades y escoger la que conlleve menos kilómetros, lo cual no parece complicado. Sin embargo, un cálculo rápido nos hace ver que para recorrer N ciudades (dejando fija la ciudad de salida y llegada, y considerando que lo mismo da recorrerlas en un sentido u otro) habría que comprobar (N-1)1/2 combinaciones distintas; esto significa que: - Con 4 ciudades => 3 recorridos distintos - Con 5 ciudades => 12 recorridos distintos - Con 6 ciudades => 60 recorridos distintos - Con 10 ciudades => 181.440 recorridos distintos - Con 20 ciudades => 60.822.550.204.416.000 recorridos distintos - Con 25 ciudades => 310.224.200.866.619.719.680.000 recorridos distintos Es decir, si hay que recorrer 25 ciudades, habría que evaluar del orden de 3.1x10* combinaciones distintas para escoger la que resulte una menor distancia. Suponiendo que nuestro ordenador fuese capaz de evaluar 1 millón de 1 de 31 EN | PROGRAMACIÓN 1 2. Grado en Física SE ¿ UY Univecsidad Facultad de Ciencias de Granada DEPARTAMENTO DE ARQUITECTURA Y TECNOLOGÍA DE COMPUTADORES Práctica 10 combinaciones por segundo, esto supondría que para evaluar todas esas posibilidades se tardaría la friolera de 9.830.035.515 años, es decir, ¡9.830 millones de años! Aunque tuviésemos un ordenador 1.000 veces más potente dentro de unas decenas de años, tardaríamos 9,8 millones de años en dar con la solución correcta. Este tipo de problemas, que tienen una complejidad de orden factorial (O(N!)) se llaman NP-completos, y es imposible solucionarlos de forma exacta cuando el valor de N es suficientemente grande. Por ello, para buscar una solución se deben emplear heurísticas o aproximaciones que aunque no nos proporcionen la mejor solución, al menos consigamos una solución razonablemente buena. Por ello, se les llama problemas de optimización, ya que en vez de tratar de encontrar la solución exacta, se busca una solución que sea óptima dada nuestra limitación de tiempo de cálculo. Fíjese que el problema del viajante de comercio es similar a varios problemas de nuestra vida diaria: - Cuando usamos el GPS o Google Maps para ir de un punto a otro. A menudo nos proponen rutas extrañas, que no son las mejores, aunque nos llevan al sitio que queremos. - Cuando hacemos reservas de vuelos, a menudo nos proponen combinaciones no demasiado buenas, con escalas intermedias que nos alejan del destino o muchas horas de espera entre escalas. 2de 31 | PROGRAMACIÓN Grado en Física Facultad de Ciencias K h E Y 2, 7 j Universidad de Granada DEPARTAMENTO DE ARQUITECTURA Y TECNOLOGÍA DE COMPUTADORES Práctica 10 / ¡Método leerMapa(): extrae los datos de. fichero y los copia /fscbre la matriz de distancias del objeto void tMapa::LeerMapa (void) t ifstream fichero; 25 E fichero.open ("distancias.txt"); //Leer número de ciudades fichero >> num_ciudades; //Leer nombre de las ciudades for(i=07 i< num_ciudades 1++) fichero >> nombre ciudad[i7; //Leer la matriz de distancias for(i=0; ¡< num_ciudades; 1++) for(j-0; j< num ciudades; ++) fichero >> distancia[i] [3]: fichero.close(); return; La clase tRuta contiene como propiedades el número de ciudades, un array que codifica el orden de recorrido de dichas ciudades usando el número de ciudad correspondiente (por ejemplo, 0-2-3-1-0 indica que se parte de la ciudad O, luego se va a la 2, después a la 3, después a la 1 y vuelta a la 0), y la distancia total que supone dicho recorrido. Además, contiene los métodos CalculaDistancia() y MuestraRuta(), que se usan para calcular la distancia que supone el recorrido codificado, y para mostrar en pantalla dicho recorrido usando el nombre de las ciudades, respectivamente. //La clase tRuta almac corrido, es decir, //el orden en que se pueden recorrer las ciudades. //Al final se añade la ciudad de origen como un elemento más. //Tambiér guarda la distancia que supone dicho recorrido. class tRuta 1 publi int num ciudades; ini recorrido[MAX-1]; int distancia; void CalculaDistancia(tMapa mapa); void MuestraRuta (tMapa mapa); 4 de 31 PROGRAMACIÓN Grado en Física Universidad de Granada Facultad de Ciencias Práctica 10 DEPARTAMENTO DE ARQUITECTURA Y TECNOLOGÍA DE COMPUTADORES /fMétodo CalculaDistancia (): calenla la distancia total //de un recorrido vold tRuta::CaloulaDistancia(tMapa mapa) t int i; distancia = (0; //Sumar la distancia entre cada dos ciudades del recorrido for (1 um_ciudades; i++) distancia=distancia+mapa.distancia[recorridoli-1]][recorridoli]]; return ; ? //Método MuestraResultado(): muestra en pantalla la distancia /fde un recorrido y el orden en que hay que recorrer las /fciudades, mostrando sus nombres. void tRuta aRuta(tMapa mapa) de int i; la << " Kms" << engl; << endl; cout << "Distancia: " << dista cout << "Recorrido propuesto: //Ciudades intermedias, cada 5 hago un sallo de línea for(i=0; i<=num_ciudades; i++) ( cout << mapa.nombre_ciudad irecorrido[i]] << "> "; if (185==0) cout << endl; ) cout << endl; return ; Sde3l PROGRAMACIÓN Grado en Física E DEPARTAMENTO DE ARQUITECTURA Y TECNOLOGÍA DE COMPUTADORES Práctica 10 E Y /, 2, 7 j Universidad de Granada Facultad de Ciencias 3.2 Algoritmo 2. Algoritmo greedy (avaricioso) Este último algoritmo pertenece a una familia de algoritmos que se denominan de tipo "greedy" (avaricioso). Es un algoritmo directo basado en una heurística que permite obtener de forma directa una buena solución, que obviamente no es la mejor, pero sí proporciona una solución aceptable. Se basa en la siguiente idea: - Dada la ciudad de origen — Repetir para todo el recorrido - Escoger la ciudad más próxima a la anterior como la siguiente De esta forma, en cada iteración se escoge como siguiente ciudad a recorrer la que esté más cerca de la anterior, es decir: - La segunda ciudad será la que quede más cerca de la ciudad de origen - Latercera ciudad será la que quede más cerca de la segunda — La cuarta ciudad será la que quede más cerca de la tercera - etc El algoritmo consiste en el fondo en una búsqueda de N mínimos, descartando las ciudades que ya han sido escogidas previamente. Este proceso es determinista, y dada la ciudad de origen, el resultado será siempre el mismo. Sin embargo, frente a los primeros algoritmos mostrados proporciona una solución bastante mejor. 7de 31 PROGRAMACIÓN Grado en Física Universidad de Granada Facultad de Ciencias DEPARTAMENTO DE ARQUITECTURA Y TECNOLOGÍA DE COMPUTADORES Práctica 10 f/Algoritmo 2 (Greedy): se establece un recorrido consistente en /fescoger en primer lugar la ciudad más cercana a la ciuda //partida, después la más cercana a la escogida, y así suces tRuta Algoritmo2 (tMapa mapa) 1 truta ruta; int distanciamin; int 1,3; int ciudad _mascercana, contador; bool udad escogida [MAX]; ruta.num_ciudades — mapa.num ciudades; //Usamos un array que indica si una ciudad ya se ha incluído o no for(i=0; i<=ruta.num_ciudades; i++) ciudad escogida[ij - false; //La primera ciudad es la de partida y la última es la misma ruta.recorrido[0] = ruta.recorrido[ruta.num ciudades] = 0; ciudad escogida[0] = ciudad escogida [ruta.num_ciudades] = true; núnero de ciudad a la que hay que buscar la má indica el número de mínimos que hay que buscar wnile (contador<(ruta.num_ciudades-1)) 1 //Para cada ciudad del recorrido busco la ciudad más cercana distanciamin — 100000; for(j=0;j azar) RutaM: r = RutaNueva; T= 0.995*1; tfin = clock(); (int) (tfin-tini) /CLOCKS_PER_SEC; nunsec ) while (mumsecs<=tiempo_maximo); return RutaMejor; ) 14 de 31 PROGRAMACIÓN 4 Grado en Física » Y 4d Y | Universidad Facultad de Ciencias de Granada DEPARTAMENTO DE ARQUITECTURA Y TECNOLOGÍA DE COMPUTADORES Práctica 10 Anexo 1. Programa viajante: los tres primeros algoritmos //Programa que trata de resolver el problema /del viajante de comercio. ueban varios al tincludo include +include include tinclude +include idotino MAX 100 define MAX TIME 23600 using namespace 57 //La clase tMapa se utiliza para almacenar en m /fel nombre ce las ciudades y las distancias entr class tMapa ( ria las mismas public: int num_ciudades; ing nombre ci stansia [MAX] [MAX] ; void LeerMapa ( hi //Mézodo LeerMapa(): extrae los datos cel fichero void LMapa: :Lee-Mape (void) ( itstroam ti int i, d: zo; fichoro.open("d: encias.txt" //teer núrero de ciudades fichero >> num ciudades; sy L4+) iudad(i]; //Leer nombre de las ciucad: is nur ciuda ero »» nombre: //leer la matriz de distancias for(i-0; i< num_cindad i++) foz(j-0; j< num ciudades; ++) fichero >> distancia[i]l(3); fichero.close(); relurn; 16 de 31 PROGRAMACIÓN Grado en Física Universidad Facultad de Ciencias de Granada DEPARTAMENTO DE ARQUITECTURA Y TECNOLOGÍA DE COMPUTADORES Práctica 10 //La clase LRuza almacene un posible recorrido, es « //el orden en que se pueden zecorzer las ciudades. //AL final se eñaco la ciudac ce origen como un elemento més. //Tamsién guarda la distancia que supone dicho recorrido. class tRuzta ( public: int num_ciudades int recorrido [MAX+1]; int distancia; void CalculaDistanc void Mues a(zMapa mapa); rakuza (=Mapa mapa); bh calcula la distancia total do un recorrido //Mézodo Calculadistancia () ia (tMapa mapa) void tRuta lculadi no distancia = ( /fSumar la distancia e casa dos ciudades del recorrido for(i=1; i<=nur_ciudades; 1++) distancia istenoia + mape.distancia [recorrido(i 11) [recorrido[i11; return ; ' //Mézodo MuestraResultado(): muestra en panzalla la distancia de un recorrido y //el orden en que hay que recorrer las ciudades, mostrando sus nombres. void tRuta::MuestraRuta (Mapa mapa) ( int i; coul << "DislLancia: " << dislancia << " Kms" << engl; cout << do propuesto: " << endl; //Ciudades ia-ermedias, cada 5 hago un sallo de linea for(i=0; i<=num ciudades; i++) t cout << ma 1F (135: nombre_ciudac[recorrido[i]] << " > "¿ =0) couz << endl; cut << endl; roturn ; , apa mapa); tRuta Algozizmol ( char EscogeAlgorizio (void); 17 de 31 PROGRAMACIÓN Grado en Física Universidad de Granada Facultad de Ciencias DEPARTAMENTO DE ARQUITECTURA Y TECNOLOGÍA DE COMPUTADORES Práctica 10 //algorilmo 2 (Greedy): se eslablece un recozrido consis.enle en / escoger srimer lugar la ciudad més cercana a la ciudad de par /facspués le més cercana a la escogida, y así sucesivamente. //Este algoritmo es en el fondo un problema de encontrar N mínimos, teniendo //en cuenta que las ciudades que ya se heyan escogido no hay que volver //a considerarlas da, tButa Algor “apa mapa) ( Ruta zuza; int distanciamin; St dee int ciudad mascercena, contador; bool ciudad escogida [MAX]; ruta.num_ciudades = mars.num_ciudades; //Usamos un arzey que indigue si una ciudad ya so ha incluído o no for(i=0; i<=ruta.num_ciudad +++) ciudad escogida[1] = false; //La primera ciucad es la ce partica y la úlzima es la misma rula.recorrido[0] = rula.reco-rido[rula.num ciudades] = 0; ciudad escogida[0] = ciudad escogida[zuza.num ciudades] = true; //i indica el número de ciudad a la que hay que buscar la más cercana 1=0; //conzador indica ol número de mínimos cue hay cue buscar, uno por cada //ciudad a recorrer, menos la última intermediz, que retorna siempre a la de /Iparzida contador= while (contador< (ru í 2a.num_ciudades-1)) //Para cada ciudad del recorrido distanciamin - 100000; for(j-0;j> eleccion; bshile ((eleccion<'1) 1] teles cout << endl; cion>'31));7 return eleccion; 20 de 31