You are on page 1of 21

Mario Rossainz Lpez

Branch & Bound y El Agente Viajero

BRANCH & BOUND Y EL PROBLEMA DEL AGENTE VIAJERO

1.- Introduccin: Uno de los problemas ms interesantes que se han tratado de resolver con diversas tcnicas algortmicas y heursticas planteadas es El problema del Agente Viajero (the Traveling Salesman Problem) por ser un problema de complejidad NP-Completo. El problema del agente viajero dice lo siguiente: Se conocen las distancias entre un cierto nmero de ciudades. Un agente viajero debe, a partir de una de ellas, visitar una ciudad exactamente una vez y regresar al punto de partida habiendo recorrido en total la menor distancia posible. Formalmente el problema puede ser enunciado como sigue: dado un grafo g conexo y ponderado y dado uno de sus vrtices v0, encontrar el ciclo Hamiltoniano de costo mnimo que comienza y termina en v0 [GUE99]. En la literatura e investigacin podemos encontrar distintas propuestas de solucin a este problema utilizando diversos paradigmas dentro del anlisis y diseo de algoritmos. El paradigma de los algoritmos vidos, la programacin dinmica, la vuelta a atrs y la ramificacin y poda son tcnicas algortmicas que han sido utilizadas para encontrar algoritmos de solucin al problema en cuestin, sin embargo debido a la complejidad de ste, hasta hoy no se ha encontrado un algoritmo que resuelva totalmente un problema NP-Completo. Es por eso que sigue siendo de inters el estudio de este tipo de problemas y dentro de la investigacin de los CPANS que he estado realizando desde hace ms de un ao a la fecha, se ha planteado la posibilidad de tener una implementacin paralela del problema del Agente Viajero utilizando como tcnica algortmica de solucin la de Ramificacin y Poda (Branch and Bound) por ser la tcnica de estudio en mi investigacin vista como una Composicin Paralela de Alto Nivel. 2.- La tcnica de Ramificacin y Poda: Es una tcnica de diseo algortmica cuyo nombre en espaol proviene de Branch and Bound en el idioma Ingles y se aplica normalmente en la solucin de problemas de optimizacin donde la complejidad computacional es grande. Ramificacin y Poda es una variante de la tcnica de Vuelta Atrs, siendo similar a sta ltima en que se realiza una enumeracin parcial del espacio de soluciones del problema basndose en la generacin de un rbol de expansin. Sin embargo la diferencia radica en que en Ramificacin y Poda existe la posibilidad de generar nodos siguiendo distintas estrategias y en Vuelta Atrs no [GUE99]. El diseo de Ramificacin y Poda puede seguir un recorrido de su rbol de expansin en anchura (estrategia LIFO), en profundidad (estrategia FIFO), o utilizando el clculo de funciones de costos para seleccionar el nodo que en principio parezca ms prometedor a analizar (estrategia del mnimo costo o LC).

Mario Rossainz Lpez

Branch & Bound y El Agente Viajero

Adems de estas estrategias Ramificacin y Poda utiliza cotas para hacer el podado de las ramas del rbol de expansin que conduzcan a la solucin ptima. Para ello se calcula en cada nodo una cota del posible valor de aquellas soluciones alcanzables desde se nodo. Si la cota muestra que cualquiera de estas soluciones tiene que ser necesariamente peor que la mejor solucin hallada hasta ese momento, no se necesita seguir explorando por esa rama del rbol, lo que permite llevar a cabo el proceso de poda. Dentro de esta tcnica, para determinar en cada momento que nodo ser ramificado y dependiendo de la estrategia de bsqueda utilizada, se requerir almacenar todos los nodos que no hayan sido podados, es decir, aquellos con posibilidad de ser ramificados (nodos vivos), en alguna estructura de datos que podamos recorrer. Se utilizar una PILA de nodos generados que todava no han sido examinados si la estrategia de bsqueda seleccionada es la de bsqueda en profundidad (LIFO). Pero si la estrategia seleccionada ha sido la de bsqueda en amplitud (FIFO), entonces la estructura de datos a utilizar ser una COLA. Por el contrario, si la estrategia elegida es la del mnimo costo (LC), la estructura necesaria de uso ser un Montculo (HEAP) para poder almacenar los nodos ordenados por su costo. La estrategia del mnimo costo utiliza una funcin de costos que decide en cada momento qu nodo debe explorarse, con la esperanza de alcanzar lo ms rpidamente posible una solucin ms econmica que la mejor encontrada hasta ese momento. 2.1.-El Algoritmo de Ramificacin y Poda: En un algoritmo de ramificacin y poda se llevan a cabo bsicamente tres etapas: La etapa de Seleccin La etapa de Ramificacin La etapa de Poda

La etapa de Seleccin: Se encarga de extraer un nodo de entre el conjunto de los nodos que no han sido podados y que tienen la posibilidad de ser ramificados. La forma de eleccin depende directamente de la estrategia de bsqueda que se decida utilizar en el algoritmo. La etapa de Ramificacin: Se construyen los posibles nodos hijos del nodo seleccionado en el paso anterior, formando el rbol de expansin. La etapa de Poda: Se eliminan algunos de los nodos creados en la etapa anterior, aquellos cuyo costo parcial sea mayor que la mejor cota mnima calculada en ese momento. La contribucin de ste algoritmo es la disminucin en lo posible del espacio de bsqueda y por tanto la atenuacin de la complejidad en la exploracin del rbol de posibilidades de la solucin ptima. Aquellos nodos no podados pasan a formar parte del conjunto de nodos con posibilidad de ser ramificados, y se comienza de nuevo el proceso de seleccin. El algoritmo finaliza una vez encontrada la solucin del problema o bien cuando se agota el conjunto de nodos con posibilidad de ser ramificados.

Mario Rossainz Lpez

Branch & Bound y El Agente Viajero

Para cada nodo del rbol de expansin se dispondr de una funcin de costo que estime el valor ptimo de la solucin si se continuara por esa rama o camino. De esta forma, si la cota que se obtiene para un nodo, es peor que una solucin ya obtenida por otra rama, se poda esa rama, pues no es interesante seguir por ella. Las funciones de costo son funciones crecientes respecto a la profundidad del rbol. Con todo esto se afirma que el valor de la tcnica de Ramificacin y Poda esta en la posibilidad de tener varias formas de exploracin del rbol y al mismo tiempo el acotamiento de la bsqueda de solucin, lo cual se traduce en eficiencia!!! [GUE99]. Sin embargo la dificultad del algoritmo radica en encontrar una buena funcin de costo para el problema a resolver, donde buena es en el sentido de que garantice la poda y que su clculo no sea muy costoso. La ventaja aadida a los algoritmos de ramificacin y poda es la de posibilitar su implementacin y ejecucin de forma paralela. Puesto que se tiene un conjunto de nodos vivos, es decir, con posibilidad de ser ramificados, sobre los que se efectan las tres etapas antes mencionadas del algoritmo, nada impide tener ms de un proceso trabajando sobre este conjunto de nodos, extrayndolos, expandindolos y llevando a cabo la poda. Los CPANs en principio podran proporcionar los algoritmos paralelizables necesarios para resolver problemas como el del Agente Viajero con la tcnica de Ramificacin y Acotacin. Sobre todo porque el abordar este tipo de problemas de forma paralela, se hace necesario si lo que se quiere adems de obtener la solucin es la de resolver el problema en tiempos razonables ya que la complejidad de dicho problema es intrnseca. Esto ltimo requiere de ciertos requerimientos que respecto a otras tcnicas algortmicas resultan caros, por ejemplo, aquellos que tienen que ver con la memoria. Se necesita por tanto, que cada objeto nodo sea autnomo, es decir, que contenga toda la informacin necesaria para que siendo objeto activo (esto es, teniendo capacidad de ejecucin en si mismo) realice los procesos de ramificacin y poda para la reconstruccin de la solucin encontrada hasta ese momento. 3.- Paralelizacin de la tcnica de Ramificacin y Poda usando el estndar POSIX Threads Existen varios esquemas para la paralelizacin de los algoritmos de Ramificacin y Poda dependiendo de las caractersticas de las diferentes arquitecturas paralelas que se tengan [CAP]. Pero en resumen los esquemas se clasifican en dos grandes grupos: 1.- Esquemas Basados en Memoria Compartida 2.- Esquemas Basados en Sistemas Distribuidos La implementacin paralela que aqu se muestra se basa en el primer tipo de esquemas, suponiendo una lista de nodos activos donde la comunicacin entre ellos se hace a travs de variables que utilizan una memoria comn. Uno de los mayores problemas de la tcnica de Ramificacin y Poda es la implementacin de los algoritmos que la disean. Sin embargo, el uso del paradigma de

Mario Rossainz Lpez

Branch & Bound y El Agente Viajero

la orientacin a objetos hace menos difcil esta tarea gracias al uso de sus propiedades como son la herencia y la modularidad. La implementacin concurrente de Ramificacin y Poda consta de los siguientes mdulos principales: 1.- Se dispondr del mdulo que contiene el programa principal donde se construye el problema del agente viajero y se resuelve de forma concurrente. En este modulo se crear la estructura de datos compartida por todos los nodos del rbol de expansin. Cada nodo que sea un nodo vivo, es decir, prximo a ser expandido ser obtenido de dicha estructura y convertido a un hilo para ser lanzado en paralelo con el resto de nodos almacenados en la estructura. El proceso se repetir mientras la estructura no este vaca o bien se haya llegado a la solucin ptima. 2.- El mdulo hilo.h es un archivo de encabezado que contiene la clase abstracta CHilo con un mtodo virtual puro fnHilo que se corresponder con el hilo de ejecucin. De esta manera cuando se quiera utilizar la clase CHilo se tendr que escribir una clase derivada de ella y redefinir el mtodo fnHilo. El cuerpo de ste mtodo ser el cdigo que ejecutar el hilo [CEB03]. 3.- El mdulo Nodos.h contiene la clase Nodos que hereda de CHilo e implementa por tanto el mtodo virtual puro fnHilo. Cualquier instancia de esta clase ser un hilo que contendr el esquema de funcionamiento general de los algoritmos que disean la tcnica de Ramificacin y Poda, en este caso para resolver el problema del agente viajero. 4.- El mdulo Nodo.h contiene la clase Nodo que describe e implementa las estructuras de datos que conforman a los nodos que sern los hilos de ejecucin. 5.- Finalmente la estructura de datos que se maneja para almacenar los nodos (hilos) que se van generando, es un montculo, pues la estrategia de bsqueda seleccionada en esta implementacin ha sido la del mnimo costo (LC), aunque como ya se mencion se podra utilizar una pila o una cola como estructura de datos segn se siga una estrategia LIFO o FIFO respectivamente.

3.1.- La Clase CHilo: La clase CHilo proporciona mtodos para iniciar un hilo, para que otro hilo pueda esperar a que ste finalice, tiene un constructor que permite asignar un nombre a un hilo y establecer los atributos con los que ste se crear, un mtodo para acceder al nombre del hilo, uno ms para obtener el identificador de ste, un mtodo para asignacin de atributos y un mtodo para modificar el nombre del hilo.

Mario Rossainz Lpez

Branch & Bound y El Agente Viajero

class CHilo { private: string m_nombreHilo; pthread_t m_idHilo; pthread_attr_t *m_attr; static void *ejec_hilo(void *arg); protected: virtual void *fnHilo() = 0;

// // // //

nombre del hilo identificador del hilo atributos del hilo invoca a fnHilo

// hilo de ejecucin

public: CHilo(string nom = "", pthread_attr_t *attr = NULL); virtual ~CHilo(); void iniciar(); // inicia el hilo void *esperar_finalizacion(); // espera a que el hilo finalice string obtener_nombre() const; // devuelve m_nombreHilo void asignar_nombre(string); // asigna el nombre del hilo pthread_t obtener_id() const; // devuelve m_idHilo void asignar_atributos(pthread_attr_t *attr); // atributos };

La definicin de la clase CHilo define los siguientes mtodos [CEB03]:

Mtodo CHilo ~CHilo Ejec_hilo

fnHilo

Iniciar esperar_finalizacion

obtener_nombre asignar_nombre obtener_id asignar_atributos

Propsito Es el constructor de la clase. Inicia los datos nombre y atributos del hilo. Si los atributos no se especifican, se utilizan los definidos por omisin Es el destructor de la clase. Finaliza el hilo slo en caso de que no lo haya hecho por s mismo Es un mtodo esttico y privado. Corresponde a la funcin que tiene que ejecutar el hilo. Esta funcin recibe como parmetro la direccin (this) del objeto que encapsula al hilo, con el fin de poder invocar a la funcin virtual fnHilo Es un mtodo virtual puro que hace que la clase CHilo sea abstracta. Esto hace necesario derivar una clase concreta de esta redefiniendo ste mtodo, donde se escribir el cdigo que ejecutar el hilo. Inicia la ejecucin del hilo representado por el objeto CHilo que reciba este mensaje. Esto es, se lanza el hilo. Este mtodo permite, al hilo que lo invoca, esperar a que el hilo que recibe el mensaje finalice su ejecucin. El valor devuelto hace referencia al valor devuelto por el hilo, o en su defecto al estado del mismo. Es el mtodo que devuelve el nombre del hilo Es el mtodo que asigna un nombre al hilo Es el mtodo que devuelve el identificador del hilo Es el mtodo utilizado para asignar los atributos al hilo

Mario Rossainz Lpez

Branch & Bound y El Agente Viajero

El cdigo que corresponde a cada uno de los mtodos descritos es el siguiente:


// Constructor CHilo::CHilo(string nom, pthread_attr_t *attr) : m_nombreHilo(nom), m_idHilo(0), m_attr(attr) { } // Destructor CHilo::~CHilo() { if (m_idHilo) // si el hilo existe, finalizarlo { int cod = pthread_cancel(m_idHilo); //terminar el hilo m_idHilo if (cod) throw(string("error: pthread_cancel")); m_idHilo = 0; } } // Crear el hilo que ejecutar el mtodo static ejec_hilo, // el cual recibe como argumento this. void CHilo::iniciar() { if (m_idHilo) return; int cod = pthread_create(&m_idHilo, m_attr, ejec_hilo, this); if (cod) throw(string("error: pthread_create")); } // El mtodo ejec_hilo invoca a fnHilo, mtodo virtual puro // que el usuario debe redefinir para escribir el cdigo que // ejecutar el hilo. void *CHilo::ejec_hilo(void *arg) { CHilo *pObj = reinterpret_cast<CHilo *>(arg); // this return pObj->fnHilo(); } // Mtodo que esperar a que m_idHilo finalice void *CHilo::esperar_finalizacion() { void *vr; int cod = pthread_join(m_idHilo, &vr); if (cod) throw(string("error: pthread_join")); m_idHilo = 0; return vr; } string CHilo::obtener_nombre() const { return m_nombreHilo; } void CHilo::asignar_nombre(string nom) { m_nombreHilo = nom; } pthread_t CHilo::obtener_id() const { return m_idHilo; } void CHilo::asignar_atributos(pthread_attr_t *attr) { m_attr = attr; }

Mario Rossainz Lpez

Branch & Bound y El Agente Viajero

3.2.- El Programa Principal: Es el mdulo que define el problema del agente viajero y lo resuelve aplicando la tcnica de Ramificacin y Poda en su versin concurrente. Se tiene una funcin llamada nodoInicial( ) que definir el nodo raz del rbol de expansin.
Nodo* nodoInicial() { Nodo *n; int i,j; mat_ady m; for(i=0;i<N;i++) m[i][i]=INFINITO;

m[0][1]=25; m[0][2]=40; m[0][3]=31; m[1][0]=5; m[1][2]=17; m[1][3]=30; m[2][0]=19; m[2][1]=15; m[2][3]=6; m[3][0]=9; m[3][1]=50; m[3][2]=24; m[4][0]=22; m[4][1]=8; m[4][2]=7; m[4][3]=10; n=new Nodo(); for(i=1;i<N-1;i++) n->s[i]=0; for(i=0;i<N;i++) for(j=0;j<N;j++) n->matriz[i][j]=m[i][j]; n->coste=n->reducir(); n->s[0]=0; for(i=1;i<N;i++) n->s[i]=-1; n->k=0; return n; }

m[0][4]=27; m[1][4]=25; m[2][4]=1; m[3][4]=6;

Este nodo inicial contendr la matriz de costos inicial que representa el grafo que define el problema y se almacenar inicialmente en la estructura montculo con un costo mnimo inicial. Mientras el montculo no este vaco se irn sacando los nodos vivos (es decir, aquellos que no hayan sido podados y que estn prximos a expandirse) del montculo y se lanzarn en paralelo para ir generando los correspondientes subrboles de expansin. Cada uno de los nodos de los subrboles de expansin generados que cumplan con la regla del costo mnimo se almacenarn en la estructura compartida, es decir, en el montculo. El uso correcto de dicha estructura por los nodos hilos esta garantizada por la implementacin de la sincronizacin de stos a travs del uso de candados y variables de condicin en base al modelo productor-consumidor.

Mario Rossainz Lpez

Branch & Bound y El Agente Viajero

int main() { int i,cont=0; Nodos *n[5000]; Nodo * aux; Nodo *raiz=nodoInicial(); estructura.insert(mmid::value_type(raiz->coste,raiz)); while(!estructura.empty()) { pthread_mutex_lock(&candado1); while((bandera)&&(!producer_done)) pthread_cond_wait(&var_cond,&candado1); if((bandera)&& producer_done) { pthread_mutex_unlock(&candado1); break; } bandera=true; pthread_mutex_unlock(&candado1); pthread_mutex_lock(&candado3); iter=estructura.begin(); pthread_mutex_unlock(&candado3); pthread_mutex_lock(&candado2); bandera2=false; pthread_cond_signal(&var_cond2); pthread_mutex_unlock(&candado2); aux=(iter->second); numanalizados++; aux->imprime_datos_nodo("Nodo"); n[cont]=new Nodos(aux); n[cont]->iniciar(); cont++; } void *sol; for(i=0;i<cont;i++) sol=n[i]->esperar_finalizacion(); aux=static_cast<Nodo *>(sol); aux->imprime_datos_nodo("Solucion"); cout << "Nodos Generados: " << numgenerados << "\n"; cout << "Nodos Analizados: " << numanalizados << "\n"; cout << "Nodos Podados: " << numpodados << "\n"; //system("pause"); return 1; }

Al final, cuando el montculo se haya vaciado por completo el programa imprime: El nmero de nodos generados en el rbol de expansin, el nmero de nodos analizados y el nmero de nodos podados. Estos datos pueden ser tiles si se quiere analizar el algoritmo y o bien se quieren comparar distintas estrategias de solucin y funciones LC. En el caso de los Nodos Generados, el valor obtenido proporciona informacin sobre el trabajo que ha tenido que realizar el algoritmo hasta encontrar la solucin. Entre ms

Mario Rossainz Lpez

Branch & Bound y El Agente Viajero

pequeo sea este valor, menos parte del rbol de expansin se habr tenido que construir y el proceso ser ms rpido. Para el caso de los Nodos Analizados, el nmero obtenido indica la cantidad de nodos que el algoritmo ha tenido que analizar. Para ello es necesario extraerlos del montculo y comprobar si sern podados o ramificados. Es deseable que este valor sea pequeo ya que es el valor ms importante pues indica el nmero de nodos del rbol de expansin que se recorren de forma efectiva. El ltimo valor, el de los Nodos Podados nos indica la efectividad de la poda y de las restricciones que se imponen al problema. Mientras mayor sea este valor, ms trabajo se ahorra al algoritmo. Cabe mencionar que esta implementacin da como resultado una solucin al problema del Agente Viajero, pero con pequeas modificaciones se puede lograr que la aplicacin proporcione todas las soluciones a dicho problema o bien encuentre la mejor de entre todas las soluciones del problema.

3.3.- La Clase Nodo Antes de comenzar con la descripcin de la clase Nodo, cabe la pena analizar la construccin del rbol de expansin para el problema del agente viajero. La solucin que se obtenga al problema ser una secuencia de decisiones, una en cada paso o etapa. La solucin al problema del agente viajero esta formada por un vector que indicar el orden en el que deben ser visitados los vrtices del grafo conexo. Cada elemento del vector contendr un nmero entre 0 y N-1, donde N es el nmero de vrtices del grafo que define el problema. Los arcos estarn definidos en una matriz de costos (no necesariamente simtrica) de valores no negativos. De esta manera, al inicio, el vector solucin estar formado por un solo elemento, el vrtice origen, que en este caso ser el 0, y en cada paso k habr que tomar la decisin de que vrtice incluir en el recorrido. Por tanto, los valores que puede tomar el elemento en la posicin k del vector (con 0 k N-1) estarn comprendidos entre 0 y N-1 pero sin poder repetirse, es decir, no puede haber dos elementos iguales en el vector. Por tanto, cada nodo podr generar hasta N-k hijos. Este es el mecanismo que construye el rbol de expansin para el problema. Ahora bien, puesto que cada nodo ser un nodo hilo, entonces, estos deben ser autnomos, es decir, cada uno de ellos debe contener toda la informacin relevante para realizar los procesos de ramificacin, poda y reconstruccin de la solucin encontrada hasta ese momento. Para ello habr que incluir en cada nodo una matriz de costos reducida. Por definicin una fila (columna) esta reducida si contiene al menos un elemento cero y el resto de los elementos son no negativos. Una matriz se dice reducida si y slo si todas sus filas y columnas estn reducidas.

Mario Rossainz Lpez

Branch & Bound y El Agente Viajero

La suma de los valores restados para obtener la matriz reducida representa el costo de la matriz. Este valor es justo el que se utilizar como funcin de costo LC para podar nodos de un rbol de expansin. A cada nodo se le asocia por tanto una matriz reducida y un costo acumulado. La definicin del tipo nodo se conforma entonces de la definicin de la clase Nodo:
typedef int solucion[N]; typedef int mat_ady[N][N]; using namespace std; class Nodo { public: int coste; mat_ady matriz; int k; solucion s;

//Coste acumulado //matriz reducida // nivel // Vector de soluciones

void imprime_coste(char *s); void imprime_nivel(char *s); void imprime_vector_sol(char *cad); void imprime_matriz_ady(char *cad); void imprime_datos_nodo(char *cad); int reducir(); int costeFil(int i); int costeCol(int j); void quitarFil(int i, int minimo); void quitarCol(int j, int minimo); };

Adems del vector de soluciones s[] y del nivel k, los otros componentes indican el costo acumulado hasta el momento (variable coste), as como la matriz reducida asociada al nodo (variable matriz). Por otro lado, los mtodos ms importantes de la clase son los siguientes: El mtodo encargado de la reduccin de la matriz de adyacencia de un nodo es:
int Nodo::reducir() { int i,j,coste,minimo; coste=0;

for(i=0;i<N;i++) { minimo=costeFil(i); if((minimo>0)&&(minimo<INFINITO)) { quitarFil(i,minimo); coste=coste+minimo; } }

10

Mario Rossainz Lpez

Branch & Bound y El Agente Viajero

for(j=0;j<N;j++) { minimo=costeCol(j); if((minimo>0)&&(minimo<INFINITO)) { quitarCol(j,minimo); coste=coste+minimo; } } return coste; }

Para que el mtodo logre la reduccin de la matriz, ste se basa en los mtodos que calculan el mnimo de una fila y se lo restan a los elementos de dicha fila, y de forma anloga para las columnas:
int Nodo::costeFil(int i) { int j,c; c=matriz[i][0]; for(j=1;j<N;j++) { if (matriz[i][j]<c) c=matriz[i][j]; } return c; } int Nodo::costeCol(int j) { int i,c; c=matriz[0][j]; for(i=1;i<N;i++) { if (matriz[i][j]<c) c=matriz[i][j]; } return c; } void Nodo::quitarFil(int i, int minimo) { int j; for(j=0;j<N;j++) { if (matriz[i][j]<INFINITO) matriz[i][j]=matriz[i][j]-minimo; } } void Nodo::quitarCol(int j, int minimo) { int i; for(i=0;i<N;i++) { if (matriz[i][j]<INFINITO) matriz[i][j]=matriz[i][j]-minimo; } }

11

Mario Rossainz Lpez

Branch & Bound y El Agente Viajero

El mtodo imprime_datos_nodo( ), muestra en pantalla los valores que se tengan en ese momento de un objeto nodo: su costo acumulado, el nivel en donde se encuentra el nodo en el rbol de expansin, su vector de solucin parcial si el nodo no es nodo hoja o total si lo es y su matriz de adyacencia ya reducida. Para ello se vale de los mtodos correspondientes a cada uno de esos datos.
void Nodo::imprime_datos_nodo(char *cad) { imprime_coste(cad); imprime_nivel(cad); imprime_vector_sol(cad); imprime_matriz_ady(cad); } void Nodo::imprime_coste(char *s) { cout << "El coste del nodo " << s << " es: " << coste << "\n"; } void Nodo::imprime_nivel(char *s) { cout << "El nivel del nodo " << s << " es: " << k << "\n"; } void Nodo::imprime_vector_sol(char *cad) { int i; cout << "Vector de solucion del nodo " << cad << ": "; for(i=0;i<N;i++) cout << s[i] << " "; cout << "\n"; } void Nodo::imprime_matriz_ady(char *cad) { int i,j; cout << "Matriz de costes reducida del nodo " << cad << ":\n"; for(i=0;i<N;i++) { for(j=0;j<N;j++) cout << matriz[i][j] << " "; cout << "\n"; } }

12

Mario Rossainz Lpez

Branch & Bound y El Agente Viajero

3.4.- La Clase Nodos Es la clase que genera los objetos Nodos que sern los nodos hilos que contienen el esquema del funcionamiento general de los algoritmos que disean la tcnica de Ramificacin y Poda para la solucin del problema del agente viajero. Este esquema se conforma de los tipos de datos abstractos que representan los nodos y de la estructura de datos para su almacenamiento.

pthread_mutex_t candado1=PTHREAD_MUTEX_INITIALIZER; pthread_cond_t var_cond=PTHREAD_COND_INITIALIZER; bool bandera=false; pthread_mutex_t candado2=PTHREAD_MUTEX_INITIALIZER; pthread_cond_t var_cond2=PTHREAD_COND_INITIALIZER; bool bandera2=true; pthread_mutex_t candado3=PTHREAD_MUTEX_INITIALIZER; int producer_done=0; int numhijos; int numanalizados=0,numgenerados=0,numpodados=0; typedef multimap<int, Nodo *, less<int> > mmid; mmid estructura; mmid::const_iterator iter;

using namespace std; class Nodos:public CHilo { public: static int cota; Nodo *node; Nodo *nhijos[MAXHIJOS]; Nodos(Nodo *aux); int reducir(mat_ady m); int expandir(); bool aceptable(Nodo *n); bool esSolucion(Nodo *n); void poner_cota_sup(int numhijos); void imprime_cota_sup(); private: Nodo * copiar(Nodo *n); protected: void * fnHilo(); };

13

Mario Rossainz Lpez

Branch & Bound y El Agente Viajero

La clase Nodos ha de implementar entonces los siguientes mtodos:


Mtodo expandir Propsito Es el mtodo que construye los nodos hijos de un nodo dado y devuelve el nmero de hijos que ha generado. Esta funcin es la que lleva a cabo el proceso de ramificacin del algoritmo Branch & Bound Es el mtodo que realiza la poda del algoritmo Branch & Bound. Dado un nodo vivo, decide si se analiza o se rechaza y se poda. Es el mtodo que decide cuando un nodo es una hoja del rbol, es decir, una posible solucin al problema original. No obstante esta solucin no necesariamente es la mejor, simplemente es cualquier solucin. Mtodo que se utiliza para definir la cota superior del problema. Normalmente el mtodo que realiza la poda utiliza este dato para podar aquellos nodos cuyo valor sea superior a la cota ya obtenida de una solucin. Mtodo que imprime en pantalla el valor de la cota superior de cada uno de los nodos que van conformando el camino de solucin del problema. Mtodo auxiliar utilizado por el mtodo que lleva a cabo el proceso de ramificacin de un nodo, y que es necesario para trabajar localmente con los datos del nodo en cuestin ya que el acceso inicial a l se hace por referencia. El mtodo copia los datos del nodo a expandir a un objeto nodo auxiliar. Es el cdigo que ejecutar cualquier nodo hilo que se lance a ejecucin. En este mtodo se implementa la tcnica de Branch & Bound a travs de las tres etapas ya mencionadas: la etapa de seleccin, la etapa de ramificacin y la etapa de la poda. Esta parte de cdigo esta sincronizada con candados y variables de condicin siguiendo el modelo de productor-consumidor en el uso del dato compartido que es el montculo utilizado para almacenar los nodos vivos del rbol de expansin.

aceptable esSolucion

poner_cota_sup

Imprime_cota_sup copiar

fnHilo

El cdigo que corresponde a cada uno de los mtodos descritos es el siguiente: La estrategia de ramificacin esta a cargo de la funcin expandir. Cada nodo genera en esta etapa a lo ms N-k hijos donde N es el nmero total de vrtices del grafo y k es un nmero que esta entre 1 y N. Esos N-k hijos son los correspondientes a los vrtices aun no incluidos en el recorrido y por tanto debern ser vrtices no repetidos.
int Nodos::expandir() { int nk,i,j,l,coste,nh,indice,aux1,aux2; Nodo *p; bool band; nh=0; nk=node->k+1; i=node->s[nk-1]; if (nk>(N-1)) return nh; for(j=0;j<N;j++) { band=false; for(indice=0;indice<=(nk-1);indice++) { if(node->s[indice]==j) { band=true; break; } }

14

Mario Rossainz Lpez

Branch & Bound y El Agente Viajero

if (!band) { nh++; p=copiar(node); p->s[nk]=j; if (nk==N) p->coste=p->coste+(node->matriz[i][j]+ node->matriz[j][0]); else { for(l=0;l<N;l++) { p->matriz[i][l]=INFINITO; p->matriz[l][j]=INFINITO; } p->matriz[j][0]=INFINITO; p->coste=p->coste+p->reducir()+node->matriz[i][j]; } p->k++; nhijos[nh-1]=p; } } return nh; }

En este mtodo se hace uso de un mtodo que permite duplicar un nodo:


Nodo * Nodos::copiar(Nodo *n) { int i,j; Nodo *p=new Nodo(); for(i=0;i<N;i++) p->s[i]=n->s[i]; for(i=0;i<N;i++) for(j=0;j<N;j++) p->matriz[i][j]=n->matriz[i][j]; p->coste=n->coste; p->k=n->k; return p; }

Cabe sealar que en el mtodo expandir se verifica si un vrtice del grafo ya esta incluido en el recorrido solucin del problema para evitar as la duplicidad de vrtices en dicho camino. Esta parte podra implementarse como un mtodo auxiliar Honesta tal como lo muestra el cdigo siguiente:
bool Nodos::noEsta(int *s,int k, int j) { int i; for(i=0;i<=k;i++) { if (s[i]==j) return false; } return true; }

15

Mario Rossainz Lpez

Branch & Bound y El Agente Viajero

Tambin se hizo necesario la implementacin del mtodo que realiza la poda. El mtodo siguiente poda aquellos nodos cuyo valor hasta el momento supere el alcanzado por una solucin ya encontrada.
bool Nodos::aceptable(Nodo *n) { return (n->coste<=cota); }

El valor que contiene el objeto nodo en su variable coste representa el costo acumulado hasta ese momento. El sentido de este valor es encontrar siempre la solucin con un menor costo. Otro mtodo necesario es el que determina cuando un nodo es una solucin. Para el caso del problema en cuestin ste mtodo consiste en decidir cuando hemos sido capaces de acomodar hasta el N-simo vrtice en el camino de solucin.
bool Nodos::esSolucion(Nodo *n) { return (n->k==N-1); }

Existe tambin el mtodo que define la cota superior del problema. Este valor que es calculado por el mtodo que se enuncia en estas lneas es el que utiliza el mtodo aceptable para decidir si podar o no el nodo que se esta analizando.
void Nodos::poner_cota_sup(int numhijos) { int minimo=INFINITO,i; for (i=0;i<numhijos;i++) { if (minimo>nhijos[i]->coste) minimo=nhijos[i]->coste; } cota=minimo; }

Un mtodo puramente auxiliar es el mtodo que imprime el valor de la cota superior calculado por el mtodo anterior.
void Nodos::imprime_cota_sup() { cout << "COTA SUPERIOR: " << cota << "\n"; }

Finalmente el mtodo fnHilo contiene la implementacin concurrente de la tcnica Branch & Bound para la solucin del agente viajero en base al esquema que encuentra la mejor solucin de entre todas las soluciones posibles.

16

Mario Rossainz Lpez

Branch & Bound y El Agente Viajero

void * Nodos::fnHilo() { int i,j; numhijos=expandir(); numgenerados=numgenerados+numhijos; pthread_mutex_lock(&candado3); estructura.erase(estructura.begin()); pthread_mutex_unlock(&candado3); cout << "NUMERO DE HIJOS: " << numhijos << "\n"; poner_cota_sup(numhijos); imprime_cota_sup(); for(i=0;i<numhijos;i++) { if (aceptable(nhijos[i])) { if (esSolucion(nhijos[i])) { for(j=0;j<numhijos;j++) if (i!=j) delete nhijos[j]; numpodados=numpodados+estructura.size(); pthread_mutex_lock(&candado3); estructura.clear(); pthread_mutex_unlock(&candado3); pthread_mutex_lock(&candado1); producer_done=1; pthread_cond_broadcast(&var_cond); pthread_mutex_unlock(&candado1); pthread_exit(nhijos[i]); } else { pthread_mutex_lock(&candado2); while(bandera2) pthread_cond_wait(&var_cond2,&candado2); bandera2=true; pthread_mutex_unlock(&candado2); pthread_mutex_lock(&candado3); estructura.insert(mmid::value_type (nhijos[i]->coste,nhijos[i])); pthread_mutex_unlock(&candado3); pthread_mutex_lock(&candado1); bandera=false; pthread_cond_signal(&var_cond); pthread_mutex_unlock(&candado1); } } else { delete nhijos[i]; numpodados++; } } pthread_exit(NULL); }

17

Mario Rossainz Lpez

Branch & Bound y El Agente Viajero

El cdigo que se muestra a continuacin define la variable cota que representa la cota superior en cada nodo que se analiza, as como el constructor de la clase que permite crear objetos nodos que son hilos de ejecucin.
int Nodos::cota=INFINITO; Nodos::Nodos(Nodo *aux) { node=aux; }

Es importante sealar que no se consider la posibilidad de que no existiera una solucin al problema del agente viajero, ya que para este problema eso nunca ocurre pues se esta suponiendo que el grafo con el que se trabaja es conexo y por tanto, siempre existe al menos una solucin que conecta a todos los vrtices entre s. Con ello finaliza la implementacin concurrente de la tcnica de Ramificacin y Poda para la solucin del problema del Agente Viajero. Como ejemplo de ejecucin de la aplicacin se consider el siguiente grafo tomado de la referencia [GOO77]:
0

27

25

22
4

19

5 8
1

25 7 6 31 1

40 50 30 17 15

10

24

El grafo representa una red de 5 ciudades, cuya matriz de costos es la siguiente:

0 1 2 3 4

0 5 19 9 22

1 25 15 50 8

2 40 17 24 7

3 31 30 6 10

4 27 25 1 6

18

Mario Rossainz Lpez

Branch & Bound y El Agente Viajero

La solucin obtenida por el programa implementado es:

19

Mario Rossainz Lpez

Branch & Bound y El Agente Viajero

Cuya solucin encontrada fue el camino 0, 3, 4, 2, 1, 0 con un costo de 64, que es la misma encontrada en el ejemplo de [GOO77]:
27
0

25

22
4

19

5 8
1

25 7 6 31 1

40 50 30 17 15

10

24

4.- Trabajos Futuros: Probar la aplicacin concurrente de la tcnica de Ramificacin y Poda con el problema del agente viajero en el cluster de Barcelona. Adecuar esta implementacin a los CPANS de forma que el problema pueda ser resuelto mediante el uso de CPANS ya definidos como son los Farms, Cauces y Arboles y con ello demostrar la eficiencia de la aplicacin. Concluir y entregar resultados de rendimiento o speedup.

5.- NOTA IMPORTANTE: Cabe sealar que se ha modificado la implementacin de los CPANS utilizando la idea de heredar de la clase CHilo propuesta en [CEB03]. De esta manera se abstraen las caractersticas de un objeto Hilo y se particulariza o se concretiza en cada uno de los CPANS implementados de forma que todo objeto Farm, Pipe o Tree sigue siendo un hilo activo. Con ello, en mi opinin, la implementacin de los CPANs se hace ms clara.

20

Mario Rossainz Lpez

Branch & Bound y El Agente Viajero

6.- REFERENCIAS BIBLIOGRAFICAS [BRA98] Brassard G., Bratley P. Fundamentos de Algoritmia. Prentice may. Espaa 1998. ISBN: 84-89660-00-X [BEL01] Bello L. P., DeIta L.G., Diseo de Heursticas para resolver el problema del Agente Viajero. Tesis de Licenciatura. Puebla, Mxico 2001. Proyecto Financiado por CONACYT, con nmero de registro 28025-A. [CAP] Capel, T.M. Tesis de Doctorado. Captulo IV: Paralelizacin de esquemas secuenciales. Seccin IV.4.: Paralelizacin del Mtodo de Ramificacin y Acotacin. [CAP92] Capel T.M., Palma A., A Programming tool for Distributed Implementation of Branch-and-Bound Algorithms. Parallel Computing and Transputer Applications. IOS Press/CIMNE. Barcelona 1992. [CEB03] Ceballos F.J., Programacin Orientada a Objetos con C++. Editorial RAMA. Espaa 2003. ISBN: 84-7897-570-5 [GOO77] GoodMan S.E., Hedetniemi S.T. Introduction to the Design and Analysis of Algorithms. Mc Graw Hill Book Company. United States of America 1977. ISBN:0-07023753-0. [GUE99] Guerrequeta G.R., Vallecillo M.A. Tcnicas de Diseo de Algoritmos. Manuales. Universidad de Malaga. [TRO92] Troya J.M., Capel T.M., An Object Based Tool for Distributed Programming on Transputer Systems

21

You might also like