05 de noviembre de 2015
Algoritmo genético para TSP en c++ Inteligencia Artificial Artificial Julcarima Calle, Josué.
Informe Algoritmo Genético
Tabla de contenido PRESENTACIÓN ............................................................................................................ 2 Resumen...................................................................................................................... 2 Introducción................................................................................................................ 2 EL PROBLEMA .............................................................................................................. 3 Especificación del problema ....................................................................................... 3 SOLUCIÓN AL PROBLEMA .......................................................................................... 4 Especificación del conocimiento declarativo ............................................................. 4 Estrategia de la solución ............................................................................................. 4 Plantear el conocimiento procedural.......................................................................... 5 Descripción del conocimiento procedural .................................................................. 5 RESULTADOS ................................................................................................................ 6 Resultados obtenidos .................................................................................................. 6 Conclusiones ............................................................................................................... 6 REFERENCIAS............................................................................................................... 7 CÓDIGO FUENTE .......................................................................................................... 8
1
Inteligencia Artificial
PRESENTACIÓN Resumen El presente informe tiene como objetivo especificar el proceso de solución llevado a cabo para resolver el problema del agente viajero por medio de un algoritmo genético e implementarlo en C++.
Introducción El problema del agente viajero es un problema NP-Hard de optimización combinatoria, el tiempo requerido para resolverlo es super-polinomial. Resolver el peor caso para un algoritmo determinístico tarda en proporción exponencial a la cantidad ciudades (un parámetro de la entrada que se explica más adelante). El problema fue formulado matemáticamente en el 1800 por el matemático irlandés WR Hamilton y por el matemático británico Thomas Kirkman.
2
Informe Algoritmo Genético
EL PROBLEMA Especificación del problema El problema del agente viajero en ciencias de la computación puede ser planteado de la siguiente manera:1 Dada una lista de las ciudades y las distancias entre cada par de ciudades, ¿cuál es el camino más corto que visita cada ciudad exactamente una vez y vuelve a la ciudad de origen?
En términos de teoría de grafos se puede plantear como sigue: Dado una grafo ponderado completo (donde los vértices representan las ciudades, las aristas representan los caminos y los pesos son el costo o las distancias de estos caminos), encontrar un ciclo de Hamilton con menor peso.
Sin embargo pueden existir variaciones al problema como por ejemplo que el grafo sea d irigido ó que el grafo no sea necesariamente completo, este último caso se puede incluir en el planteamiento original considerando que las aristas que faltan tienen peso infinito. En el caso del presente informe se resolverá el problema de forma no determinística tomando en cuenta que los puntos o ciudades pertenecen a un espacio bidimensional y el peso de las aristas es la distancia pitagórica (norma 2) entre los puntos.
1 (Wikipedia)
3
Inteligencia Artificial
SOLUCIÓN AL PROBLEMA Especificación del conocimiento declarativo Respecto a la declaración de variables de entrada se tiene:
Un conjunto de puntos todos en un espacio bidimensional.
Respecto a las variables de salida del algoritmo se tienen:
Una permutación del conjunto de puntos de la entrada denotando el camino hamiltoniano óptimo.
Estrategia de la solución Las soluciones existentes en la literatura son muy amplias y se pueden dividir en 2: 1. Determinísticas. – Es decir para una entrada dada siempre obtienen la misma salid a. o Heurísticas o Exactas.- Este tipo de algoritmos son los únicos que aseguran obtener la solución óptima 2. No determinísticas (aleatorizados).- Nótese que estos algoritmos pueden utilizar aproximaciones heurísticas en su procedimiento. Para el desarrollo del presente trabajo se escogió la estrategia no determinística del algoritmo genético.
4
Informe Algoritmo Genético
Plantear el conocimiento procedural El algoritmo planteado es a grandes rasgos:
Generación de la población inicial
Evolución de la población hasta una condición de parada predefinida
Obtención del mejor individuo de la población final Descripción del conocimiento procedural Cada uno de los pasos anteriores se especifica como sigue: 1. Generación de la población inicial a. De manera aleatoria se generan los individuos iniciales barajando la permutación (identificador de un camino/ciclo Hamiltoniano). 2. Evolución de la población hasta una condición de parada predefinida. Cada evolución a la siguiente generación consta de los siguientes pasos. a. Pase de una élite a la siguiente generación (los ELITE mejores siempre pasan a la siguiente generación). b. Cruce. Se cruzan los individuos donde la probabilidad de cada participante de un apareamiento es proporcional a su fitness (función de aptitud, la cual en el problema actual es el recíproco de la distancia). c. Mutación. Después de cada cruce se realiza una mutación en los hijos dependiendo de la probabilidad de mutación. 3. Obtención del mejor individuo de la población final. La condición de parada establecida en este caso es el número de generaciones o poblaciones ( NUMBER_OF_GENERATIONS)
5
Inteligencia Artificial
RESULTADOS Resultados obtenidos Se probó el algoritmo para el caso de xqf131 2 (la entrada posee 131 nodos y se encuentra en http://www.math.uwaterloo.ca/tsp/vlsi/xqf131.tsp) con los siguientes parámetros:
int TOTAL_POPULATION = 5000;
double MUTATION_PROBABILITY = 0.07;
int NUMBER_OF_GENERATIONS = 10000; int ELITE = 3;
Obteniendo los siguientes resultados:
Mejores distancias encontradas por generación (población) Generation
Best distance found
0
3891.81
1000
2827.73
2000
2623.44
3000
2466.63
4000
2332.91
5000
2185.81
6000
2141.97
7000
2059.38
8000
1991.88
9000
1922.1
9999
1890.8
Conclusiones Se concluye que en la mayoría de los casos a mayor tiempo gastado en buscar una solución se obtiene una solución más óptima al final. Para el caso propuesto la solución óptima tiene una distancia de 564 según 3, mientras el programa diseñado con los parámetros dados solo logró encontrar una solución con distancia de 1890. Esto comprueba que la probabilidad de necesitar un tiempo muy alto para hallar la solución óptima es muy alta.
2 (Math
U. Waterloo) TSP Problem Optimal Tour)
3 (xqf131
6
Informe Algoritmo Genético
REFERENCIAS Math U. Waterloo. (s.f.). Obtenido de http://www.math.uwaterloo.ca/tsp/vlsi/index.html Math U. Waterloo. (s.f.). Obtenido de http://www.math.uwaterloo.ca/tsp/vlsi/xqf131.tour
Wikipedia. (s.f.). Obtenido de https://en.wikipedia.org/wiki/Travelling_salesman_problem
7
Inteligencia Artificial
CÓDIGO FUENTE #include
#include #include #include #include #include #include using namespace std;
/** Parameters **/ int TOTAL_POPULATION = 500; double MUTATION_PROBABILITY = 0.07; int NUMBER_OF_GENERATIONS = 1000; int ELITE = 1; // the best ones which always pass to the next generation
struct Point{ double x, y; Point(){} Point(double _x, double _y): x(_x), y(_y) {} double dist(Point p){ return hypot(p.x - x, p.y - y ); } };
struct Population{ private:
8
Informe Algoritmo Genético vector &points; vector > routes; vector prob; int n; // size of the route
public: /** Freeing memory **/ ~Population(){ routes.clear(); prob.clear(); }
/** Constructors **/ Population(vector &_points): points(_points){} Population(int points(_points){}
routeSize,
Population(int points(_points) {
popuSize,
vector
int
routeSize,
&_points):
n(routeSize),
vector
&_points):
n = routeSize; routes.clear(); vector ind; // indexes to shuffle for(int i = 0; i < n; i++) ind.push_back(i);
// random init for(int i = 0; i < popuSize; i++){ random_shuffle ( ind.begin(), ind.end() ); vector newRoute = ind; routes.push_back( newRoute ); } }
9
Inteligencia Artificial
vector > getRoutes() const{ return routes; }
int getRouteSize() const{ return n; }
vector getProb() const{ return prob; }
void operator=(const Population& pob){ routes = pob.getRoutes(); n = pob.getRouteSize(); prob = pob.getProb(); }
// OX1 type crossover void crossover(vector &p1, vector &p2, vector &h1, vector &h2 ){ h1.clear(); h2.clear(); int a = rand()%(n-1), b = rand()%(n-1) ; if( a > b ) swap(a,b);
bool oc1[n]; bool oc2[n];
10
Informe Algoritmo Genético memset(oc1, 0, sizeof(oc1)); memset(oc2, 0, sizeof(oc2));
for(int i = 0; i < n; i++){ h1.push_back( -1 ); h2.push_back( -1 ); }
for(int i = a+1; i <= b; i++){ h1[i] = p1[i]; oc1[ p1[i] ] = 1; h2[i] = p2[i]; oc2[ p2[i] ] = 1; } int i1 = (b+1)%n, i2 = (b+1)%n; for(int i = 0; i < n; i++){ if( !oc1[ p2[ (b+1+i)%n ] ] ){ h1[i1] = p2[ (b+1+i)%n ]; i1 = (i1+1)%n; } if( !oc2[ p1[ (b+1+i)%n ] ] ){ h2[i2] = p1[ (b+1+i)%n ]; i2 = (i2+1)%n; } } /* cout << "indices: " << a << " " << b<
11
Inteligencia Artificial }*/
/** mutation **/ double r; r = (double)rand()/(double)RAND_MAX; if( r <= MUTATION_PROBABILITY ){ a = rand()%(n); b = rand()%(n); swap(h1[a], h1[b]); }
r = (double)rand()/(double)RAND_MAX; if( r <= MUTATION_PROBABILITY ){ a = rand()%(n); b = rand()%(n); swap(h2[a], h2[b]); } }
Population evolve(){ Population pSiguiente = Population(n, points); order(); int cant; for(cant = 0; cant < ELITE && cant < (int)routes.size(); cant++){ vector nRuta = routes[cant]; pSiguiente.addRoute( nRuta ); }
/** Fitness proportionate selection or roulette-wheel selection **/ for(; ELITE < (int)routes.size() ;){
12
Informe Algoritmo Genético vector h1,h2; double r;
r = (double)rand()/(double)RAND_MAX; int prob.begin();
ind1
=
lower_bound(
prob.begin(),
prob.end(),
r)
-
r)
-
int ind2 = ind1; while( ind2 == ind1){ r = (double)rand()/(double)RAND_MAX; ind2
=
lower_bound(
prob.begin(),
prob.end(),
prob.begin(); }
//cout <<"a cruzar: " << ind1<<" " << ind2 << endl;
crossover( routes[ind1], routes[ind2], h1, h2 ); pSiguiente.addRoute( h1 ); if( pSiguiente.getSize() == (int)routes.size() ) break; pSiguiente.addRoute( h2 ); if( pSiguiente.getSize() == (int)routes.size() ) break; } return pSiguiente; }
/**
This is the total distance of the i-th chromosome **/
double getNoFitness(int i){ if( i >= 0 && i < routes.size() ){ double noFitness = 0; for(int j = 0; j < n; j++){
13
Inteligencia Artificial noFitness points[routes[i][(j+1)%n] ] )
+=
points[routes[i][j]].dist(
;
} return noFitness; }else return -1.0; }
void order(){ vector > ind; double totalFitness = 0; for(int i = 0; i < (int)routes.size(); i++){ double noFitness = getNoFitness(i); double fitness = 1.0/(double)noFitness; totalFitness += fitness; ind.push_back( make_pair(-fitness, i) ); } sort( ind.begin(), ind.end() ); vector > rutas2; double ac = 0; prob.clear(); for(int i = 0; i < (int)routes.size(); i++){ rutas2.push_back(routes[ ind[i].second ]); ac+= -ind[i].first; prob.push_back( ac/totalFitness ); } routes = rutas2;
}
void addRoute(vector &nRuta){
14
Informe Algoritmo Genético routes.push_back(nRuta); }
int getSize(){ return (int)routes.size(); }
/** You have to order before calling to this method **/ vector getFirst(){ vector res; for(int i = 0; i < n; i++) res.push_back( points[ routes[0][i] ]); return res; }
vector getFirstIndexes(){ return routes[0]; }
Point getPoint(int i){ return points[i]; }
};
/** Genetic algorithm **/ vector ga_tsp(vector &points){ int n = (int)points.size();
Population pActual (TOTAL_POPULATION, n, points);
15
Inteligencia Artificial pActual.order(); vector result = pActual.getFirst(); vector rIndexes = pActual.getFirstIndexes();
cout << "Route/cycle more with more fitness on initial population with distance:"<< pActual.getNoFitness(0)<< endl; for(int i = 0; i < result.size(); i++) { cout<< rIndexes[i] << " : "<< result[i].x << " " << result[i].y << endl; }
cout << "\nGeneration" << "\t"<<"distance found" << endl;
for(int iter = 0; iter < NUMBER_OF_GENERATIONS; iter++){ Population pNext = pActual.evolve();
pActual = pNext;
pActual.order(); if(iter%100 == 0 || iter == NUMBER_OF_GENERATIONS - 1) cout <
int main(){
ios_base::sync_with_stdio(true);
srand ( unsigned ( time(0) ) );
16
Informe Algoritmo Genético
vector points;
/** Reading input **/ double x, y; while( cin >>x >> y ){ points.push_back( Point(x,y) ); } vector result = ga_tsp( points );
cout << "\nRoute/cycle more with more fitness found:"<< endl; for(int i = 0; i < result.size(); i++) { cout<< result[i].x << " " << result[i].y << endl; } }
17