Question

I'm having a problem with this code. It's a genetic algorithm I'm writing for the traveling salesman problem. However the program just crashes and doesn't complete, digging around in the debugger I found what I believe the error is however I'm not sure where it is occurring. I'll just post the whole program since I have been pouring over the code and I can't pinpoint the error. Thanks.

#include<vector>
#include<algorithm>
#include<time.h>
#include<cmath>
#include<string>
#include<iterator>

const int numcities = 15;
using namespace std;

class City{
public:
    int x,y,numcity;

    City();
    ~City();
    void setxcoord(int);
    void setycoord(int);
    int getxcoord();
    int getycoord();
    void setcitynum(int);
    int getcitynum();
};

City::City(){
    x = -1;
    y = -1;
}

City::~City(){
}

void City::setxcoord(int xcoord){
    x = xcoord;
}

void City::setycoord(int ycoord){
    y = ycoord;
}

int City::getxcoord(){
    return x;
}

int City::getycoord(){
    return y;
}

void City::setcitynum(int newcitynum){
    numcity = newcitynum;
}

int City::getcitynum(){
    return numcity;
}

class resultscontainer{
public:
    vector<City> tour;
    int i;
    vector<City>::iterator it;

    resultscontainer();
    ~resultscontainer();
    City findcity(int);
    void addcity(City);
    int toursize();

};

resultscontainer::resultscontainer(){
i = 0;
}

resultscontainer::~resultscontainer(){};

void resultscontainer::addcity(City newcity){
    it = tour.begin() + i;
    tour.insert(it,newcity);
    i++;
}

City resultscontainer::findcity(int index){
    City temp = tour.at(index);
    return temp;
}

int resultscontainer::toursize(){
    return tour.capacity();
}

class newtour{
public:
    vector<City> candidate;
    vector<City>::iterator it;
    double fitness,distance;
    string citylist;

    newtour();
    ~newtour();
    void makeindividual(resultscontainer);
    double getfitness();
    double getdistance();
    bool fulltour();
    void displaytour();
    void setuniquepos(int, City);
    City getcityatpos(int);
    bool isEmpty(int);
};

newtour::newtour(){

    City initvalue;
    for(int i =0; i < numcities ; i++){
        it = candidate.begin() + i;
        candidate.insert(it, initvalue); 
    }
}

newtour::~newtour(){};

void newtour::makeindividual(resultscontainer citylist){
    for (int i = 0; i < numcities; i++){
        it = candidate.begin() + i;
        candidate.insert(it, citylist.findcity(i));  
    }
    random_shuffle(candidate.begin(), candidate.end());
}

double newtour::getfitness(){
    fitness = 1/getdistance();
    return fitness;
}

double newtour::getdistance(){
    for(int i = 0; i+1 < candidate.capacity(); i++){
        City currentcity = candidate.at(i);
        City destinationcity = candidate.at(i+1);
        distance += sqrt(pow(currentcity.x - destinationcity.x, 2) + pow(currentcity.y - destinationcity.y, 2));
    }
    return distance;
}

bool newtour::fulltour(){
    if(candidate.capacity() == numcities) return true;
    else return false;
}

void newtour::displaytour(){
    citylist = "Tour is: ";
    for(int i = 0; i < candidate.capacity(); i++)citylist += candidate.at(i).getcitynum() + ", ";
}

void newtour::setuniquepos(int i, City newcity){
    it = candidate.begin()+i;
    candidate.insert(it, newcity);
}

City newtour::getcityatpos(int i){
    return candidate.at(i);
}

bool newtour::isEmpty(int i){
    if(candidate.at(i).getcitynum() == NULL) return true;
    else return false;
}

class population{
public:
vector< newtour > totalpop;
int index,maxpop;
vector< newtour >::iterator it;

population();
~population();
void addtour(newtour);
newtour findtour(int);
newtour findfittest();
int populationsize();
};

population::population(){
    index = 0;
    maxpop = 10;
}

population::~population(){}

void population::addtour(newtour candidate){
    it = totalpop.begin() + index;
    totalpop.insert(it, candidate);
    index++;
}

newtour population::findtour(int i){
    return totalpop.at(i);
}

newtour population::findfittest(){
    newtour fittesttour;
    for(int i = 0; i+1 < totalpop.capacity(); i++){
        newtour currenttour = totalpop.at(i);
        newtour nexttour = totalpop.at(i+1);
        if(currenttour.getfitness() <= nexttour.getfitness()) fittesttour = nexttour;
    }
    return fittesttour;
}

int population::populationsize(){
    return totalpop.capacity();
}

Above is the header file, below is the implementation.

#include"City.h"
#include<vector>
#include<algorithm>
#include<time.h>
#include<cmath>
#include<string>
#include<cstdlib>
#include<iostream>

using namespace std;

const double mutationrate = .1;
const int bracketsize = 5;

newtour bracketselection(population pop){
    population temppop;
    for(int i = 0; i < bracketsize; i++){
        int randomnum = (int) (rand() * pop.populationsize());
        temppop.addtour(pop.findtour(randomnum));
    }
    newtour best = temppop.findfittest();
    return best;
}

newtour crossover(newtour mom, newtour dad){
    newtour child;
    bool inside = false;
    int beginning, end;
    beginning = (int) (rand() * mom.distance);
    end = (int) (rand() * mom.distance);

    for(int i = 0; i < numcities; i++){
        if(beginning < end && i < end && i >beginning)child.setuniquepos(i, mom.getcityatpos(i));
        else if (beginning > end){
            if (!(i>end && i < beginning)){
                child.setuniquepos(i, mom.getcityatpos(i));
            }
    }
}
    for(int j = 0; j < numcities; j++){
        for(int k = 0; k <numcities; k++)if(child.getcityatpos(k).getcitynum() == dad.getcityatpos(j).getcitynum())inside = true;
        if(!inside)for(int l = 0; l < numcities; l++)if(child.getcityatpos(l).getcitynum() == NULL){
            child.setuniquepos(l,dad.getcityatpos(l));
            break;
        }
    }
    return child;
}

void mutation(newtour subject){
for(int i=0; i < numcities; i++){
    if(rand()* 5 / rand() < mutationrate){
        int j = (int)(bracketsize *8 %5 * rand());
        City randcity1 = subject.getcityatpos(i);
        City randcity2 = subject.getcityatpos(j);
        subject.setuniquepos(j, randcity1);
        subject.setuniquepos(i, randcity2);
        }
    }
}

population evolve(population totalpop){
    population nextgen;
    nextgen.addtour(totalpop.findfittest());
    for(int i = 1; i < nextgen.maxpop; i++){
        newtour mom,dad,child;
        mom = bracketselection(totalpop);
        dad = bracketselection(totalpop);
        child = crossover(mom,dad);
        nextgen.addtour(child);
    }
    for (int i =1; i < nextgen.maxpop; i++)mutation(nextgen.findtour(i));
    return nextgen;
}


void main(){
    City city1,city2,city3,city4,city5,city6,city7,city8,city9,city10,city11,city12,city13,city14,city15;
    population thepop;
    resultscontainer cities;

    city1.setxcoord(5);
    city1.setycoord(2);
    city1.setcitynum(1);
    city2.setxcoord(16);
    city2.setycoord(3);
    city2.setcitynum(2);
    city3.setxcoord(13);
    city3.setycoord(5);
    city3.setcitynum(3);
    city4.setxcoord(15);
    city4.setycoord(9);
    city4.setcitynum(4);
    city5.setxcoord(10);
    city5.setycoord(10);
    city5.setcitynum(5);
    city6.setxcoord(4);
    city6.setycoord(9);
    city6.setcitynum(6);
    city7.setxcoord(6);
    city7.setycoord(12);
    city7.setcitynum(7);
    city8.setxcoord(12);
    city8.setycoord(13);
    city8.setcitynum(8);
    city9.setxcoord(9);
    city9.setycoord(14);
    city9.setcitynum(9);
    city10.setxcoord(16);
    city10.setycoord(20);
    city10.setcitynum(10);
    city11.setxcoord(18);
    city11.setycoord(18);
    city11.setcitynum(11);
    city12.setxcoord(2);
    city12.setycoord(5);
    city12.setcitynum(12);
    city13.setxcoord(7);
    city13.setycoord(5);
    city13.setcitynum(13);
    city14.setxcoord(2);
    city14.setycoord(16);
    city14.setcitynum(14);
    city15.setxcoord(11);
    city15.setycoord(18);
    city15.setcitynum(15);

    cities.addcity(city1);
    cities.addcity(city2);
    cities.addcity(city3);
    cities.addcity(city4);
    cities.addcity(city5);
    cities.addcity(city6);
    cities.addcity(city7);
    cities.addcity(city8);
    cities.addcity(city9);
    cities.addcity(city10);
    cities.addcity(city11);
    cities.addcity(city12);
    cities.addcity(city13);
    cities.addcity(city14);
    cities.addcity(city15);

    thepop = evolve(thepop);
    for (int i = 0; i < 50 ; i++){
        thepop = evolve(thepop);
    }

    newtour bestroute = thepop.findfittest();

    cout << "The fittest route had a fitness of: " <<bestroute.fitness<< endl;
    cout << "The algorithm determined that the best route is :" << endl;
    bestroute.displaytour();
    cout << "With a total distance of: " << bestroute.getdistance();
}
Was it helpful?

Solution

  1. You should be using size() to get the number of elements in your containers, not capacity(). This is true for the loops, as I mentioned in the comment above, but also for population::populationsize().

  2. You call rand() and multiply it by the population size. I think you're expecting rand() to return a floating point value from 0 to 1, but it actually returns an integer from 0 to RAND_MAX (which is at least 32767). Rather than multiply it by the population size, you should modulus it by the population size:

    int randomnum = rand() % pop.populationsize();
    
  3. Modulus by zero will crash your program, just like division by zero, so you need to test that pop.populationsize() is non-zero before generating the random number in #2 above.

  4. In newtour::displaytour(), you attempt to concatenate a string with the integer candidate.at(i).getcitynum(). This won't work; you'll need to convert the integer to a string first. I'd suggest using ostringstream

  5. main() must return an int. MSVC might be letting you use void main(), but it's not standard-compliant and GCC refuses to take it.

With these fixes, your program gives the following output:

The fittest route had a fitness of: 5.50124e+306
The algorithm determined that the best route is :
With a total distance of: 1.81777e-307

It doesn't quite look right to me, but at least it compiles.

OTHER TIPS

I believe you are using rand() incorectly. For example:

int randomnum = (int) (rand() * pop.populationsize());
temppop.addtour(pop.findtour(randomnum));

randomnum will be complete nonsense and most likely accessing out of bounds in pop. I think you want something like (int)(rand() % pop.populationsize())

Also learn debugging in VS( just see your call stack, step your code and see variable values ) also use push_back instead of insert in your add...() functions.

A large part of your problem appears to be a misunderstanding - or simply lack of understanding - of how vectors work. You also seem to have a penchant for the dangerous practice of storing vector iterators as class members instead of using local variables.

Lets take a quick tour of how you use vector:

#include <iostream>
#include <string>
#include <vector>

struct Address
{
    int          m_doorNo;
    std::string  m_street;

    Address() : m_doorNo(-1), m_street("Limbo")
    {
        std::cout << "Address() default ctor called\n";
    }

    Address(int doorNo, const std::string& street)
        : m_doorNo(doorNo), m_street(street)
    {}
};

// So we can << an Address object.
static std::ostream& operator << (std::ostream& os, const Address& address)
{
    os << address.m_doorNo << " " << address.m_street.c_str();
    return os;
}

// Give our *usage* of a vector a friendly name.
typedef std::vector<Address> AddressBook;

int main()
{
    AddressBook book;
    if (book.empty())
        std::cout << "book starts empty, it has size() of " << book.size() << '\n';

    book.push_back(Address(70, "1st Street"));
    std::cout << "First address added, book.size = " << book.size() << '\n';

    book.push_back(Address(123, "Hope Avenue"));
    std::cout << "Second address added, book.size = " << book.size() << '\n';

    std::cout << "address[0] is: " << book[0] << '\n';
    std::cout << "address.at(1) is: " << book.at(1) << '\n';

    std::cout << "*address.begin() is: " << *(book.begin()) << '\n';
    std::cout << "address.front() is: " << book.front() << '\n';
    std::cout << "address.back() is: " << book.back() << '\n';

    // couple more addresses.
    book.insert(book.begin(), Address(3, "Disco Alley"));
    book.insert(book.begin() + 1, Address(5, "Mortimer Lane"));

    // overshoot, place an extra address at the end of the vector.
    book.insert(book.end(), Address(999, "Hellfire Drive"));
    // oops, lets erase that.
    book.pop_back();

    std::cout << "Done shuffling. Size = " << book.size() << " while capacity = " << book.capacity() << '\n';

    const size_t numAddrs = book.size();
    for (size_t i = 0; i < numAddrs; ++i) {
        std::cout << i << ": " << book[i] << '\n';
    }

    // and lastly, lets use a reverse iterator.
    std::cout << "backwards:" << '\n';
    // instead of std::vector<Address>::iterator etc, we can say AddressBook::iterator
    for (AddressBook::reverse_iterator it = book.rbegin(); it != book.rend(); ++it) {
        std::cout << *it << '\n';
    }

    // 'reserve' adjusts the capacity, it tells the vector to go ahead and
    // allocate space for N entries, but don't make them available yet.
    book.reserve(999);

    // 'resize' changes the size, if the new size is smaller than the capacity,
    // it forces a call to reserve to allocate more memory.
    // after that, it goes ahead and grows the array storage by default-
    // initializing any new entries.
    book.resize(9);

    std::cout << "book.size = " << book.size() << " but cap = " << book.capacity() << '\n';

    // at this point, it is safe to say:
    book[6].m_doorNo = 555;
    book[7].m_street = "Nowhere";
    book[8] = Address(10101, "Binary Bend");

    for (auto it = book.begin(); it != book.end(); ++it) {
        std::cout << *it << '\n';
    }

    return 0;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top