/* ---------------------------------------------------------------------------- ex26.C mbwall 24mar96 Copyright (c) 1995-1996 Massachusetts Institute of Technology The code for this example is adapted from an original implementation by Thomas Grueninger (from Uni Stuttgart, visiting scholar at MIT) DESCRIPTION: GAs are lousy at solving the travelling salesperson problem. But here is an example of how to do it anyway. In this case we use a steady-state genetic algorithm and give you the compile-time choice of partial match or edge recombination crossover. This one looks really good when you draw an entire population of individuals and watch them all evolve in real time. It becomes very obvious what the genetic algorithm is doing and how well the speciation is working (or not). This implementation is by no means efficient, so please do not complain about all of the silly little inefficient aspects of the implementation. But it does get the job done. ---------------------------------------------------------------------------- */ #include #include #include #include // Set this up for your favorite TSP. The sample one is a contrived problem // with the towns laid out in a grid (so it is easy to figure out what the // shortest distance is, and there are many different paths with the same // shortest path). File format is that used by the TSPLIB problems. You can // grab more problems from // // // Apologies for using fixed-length arrays. But this is an example, not // production code ;) #define MAX_TOWNS 50 #define TSP_FILE "tsp_rect_20.txt" float DISTANCE[MAX_TOWNS][MAX_TOWNS]; double x[MAX_TOWNS],y[MAX_TOWNS]; int ntowns = 0; // You can use either edge recombination crossover or partial match crossover. // Which one you select makes a HUGE difference in the performance of the // genetic algorithm. Only one of the two following lines should be commented. //#define XOVER PMXover // (Partial Match Crossover) #define XOVER ERXover // (Edge Recombination Crossover) float Objective(GAGenome&); int Mutator(GAGenome&, float); void Initializer(GAGenome&); float Comparator(const GAGenome&, const GAGenome&); int ERXover(const GAGenome&, const GAGenome&, GAGenome*, GAGenome*); int PMXover(const GAGenome&, const GAGenome&, GAGenome*, GAGenome*); void ERXOneChild(const GAGenome&, const GAGenome&, GAGenome*); int main(int argc, char** argv) { cout << "Example 26\n\n"; cout << "The Travelling Salesman Problem (TSP) demo program.\n" << endl; // See if we've been given a seed to use (for testing purposes). When you // specify a random seed, the evolution will be exactly the same each time // you use that seed number. unsigned int seed = 0; for(int ii=1; ii> dump; in >> x[ntowns]; in >> y[ntowns]; ntowns++; } while(!in.eof() && ntowns < MAX_TOWNS); in.close(); if(ntowns >= MAX_TOWNS) { cerr << "data file contains more towns than allowed for in the fixed\n"; cerr << "arrays. Recompile the program with larger arrays or try a\n"; cerr << "smaller problem.\n"; exit(1); } double dx,dy; for(int i=0;i genome(Objective); genome.initializer(::Initializer); genome.mutator(::Mutator); genome.comparator(::Comparator); genome.crossover(XOVER); GASteadyStateGA ga(genome); ga.minimize(); ga.pReplacement(1.0); ga.populationSize(100); ga.nGenerations(1000); ga.pMutation(0.1); ga.pCrossover(1.0); ga.selectScores(GAStatistics::AllScores); ga.parameters(argc, argv); cout << "initializing..."; cout.flush(); ga.initialize(seed); cout << "evolving..."; cout.flush(); while(!ga.done()) { ga.step(); if(ga.generation() % 10 == 0) { cout << ga.generation() << " "; cout.flush(); } } genome = ga.statistics().bestIndividual(); cout << "the shortest path found is " << genome.score() << "\n"; cout << "this is the distance from the sequence\n"; cout << genome << "\n\n"; cout << ga.statistics() << "\n"; return 0; } // Here are the genome operators that we want to use for this problem. // Thanks to Jan Kees IJspeert for isolating an order-of-evaluation problem // in the previous implementation of this function. float Objective(GAGenome& g) { GAListGenome & genome = (GAListGenome &)g; float dist = 0; if(genome.head()) { for(int i=0; i &child=(GAListGenome &)g; while(child.head()) child.destroy(); // destroy any pre-existing list int i,town; static int visit[MAX_TOWNS]; memset(visit, 0, MAX_TOWNS*sizeof(int)); town=GARandomInt(0,ntowns-1); visit[town]=1; child.insert(town,GAListBASE::HEAD); // the head node for( i=1; i &child=(GAListGenome &)g; register int n, i; if ((GARandomFloat() >= pmut) || (pmut <= 0)) return 0; n = child.size(); if (GARandomFloat()<0.5) { child.swap(GARandomInt(0,n-1),GARandomInt(0,n-1)); // swap only one time } else { int nNodes = GARandomInt(1,((int)(n/2-1))); // displace nNodes child.warp(GARandomInt(0,n-1)); // with or without GAList TmpList; // inversion for(i=0;i &mate1=(GAListGenome &)g1; GAListGenome &mate2=(GAListGenome &)g2; GAListGenome &sis=(GAListGenome &)*c1; int i,j,k,t1,t2,town; static char CM[MAX_TOWNS][MAX_TOWNS],visit[MAX_TOWNS]; memset(CM, 0, MAX_TOWNS*MAX_TOWNS*sizeof(char)); memset(visit, 0, MAX_TOWNS*sizeof(char)); while (sis.head()) sis.destroy(); // create connection matrix mate1.head(); for(j=0; j PossFollowList; GAList FollowersList[5]; while (PossFollowList.head()) PossFollowList.destroy(); for(k=0; k<5; k++) { while (FollowersList[k].head()) FollowersList[k].destroy(); } // select the following town with the minimal no of next folling towns int nPoss,nFollow; for(i=1; i &mom=(GAListGenome &)g1; GAListGenome &dad=(GAListGenome &)g2; int a = GARandomInt(0, mom.size()); int b = GARandomInt(0, dad.size()); int h; if (b &sis=(GAListGenome &)*c1; sis.GAList::copy(mom); GAListIter diter(dad); index = diter.warp(a); for(i=a; isize } } sis.head(); // set iterator to head of list nc += 1; } if(c2) { GAListGenome &sis=(GAListGenome &)*c2; sis.GAList::copy(mom); GAListIter diter(dad); index = diter.warp(a); for(i=a; isize } } sis.head(); // set iterator to head of list nc += 1; } return nc; } float Comparator(const GAGenome& g1, const GAGenome& g2) { GAListGenome &a = (GAListGenome &)g1; GAListGenome &b = (GAListGenome &)g2; int i,j,t1,t2; float dist=ntowns; static char CM1[MAX_TOWNS][MAX_TOWNS],CM2[MAX_TOWNS][MAX_TOWNS]; memset(CM1, 0, MAX_TOWNS*MAX_TOWNS*sizeof(char)); memset(CM2, 0, MAX_TOWNS*MAX_TOWNS*sizeof(char)); // create connection matrix CM1 a.head(); for(i=0; i::write(ostream & os) const { int *cur, *head; GAListIter tmpiter(*this); if((head=tmpiter.head()) != 0) { os << *head << " "; for(cur=tmpiter.next(); cur && cur != head; cur=tmpiter.next()) os << *cur << " "; } return os.fail() ? 1 : 0; } #ifdef NO_AUTO_INST #include #include #if defined(__GNUG__) template class GAList; template class GAListGenome; #else GAList; GAListGenome; #endif #endif