/* Genetic Algorithm - 0/1 Multi-Constraint Knapsack Problem (Multi-Dimensional) ----------------------------------------------------------------------------- Change the parameters of the genetic algorithm by changing the Globals. Author: Shalin Shah Email: shah.shalin@gmail.com ** Requirements ** The program requires an ORLIB or WEING formatted files in the current directory. Change the file name in processDataORLIB() or processDataWeing() functions. */ #include #include #include #include using namespace std; /* Class used by local Improvement */ class GreedyObject { public: double ratio; int index; public: GreedyObject(){} GreedyObject(double r, int in) { ratio = r; index = in; } GreedyObject(const GreedyObject & copy) { this->ratio = copy.ratio; this->index = copy.index; } bool operator < (const GreedyObject &right) const { return this->ratio > right.ratio; } bool operator > (const GreedyObject &right) const { return this->ratio < right.ratio; } bool operator == (const GreedyObject &right) const { return this->index == right.index; } bool operator != (const GreedyObject &right) const { return this->index != right.index; } GreedyObject & operator = (const GreedyObject ©) { this->ratio = copy.ratio; this->index = copy.index; return *this; } }; /* Globals */ int NUMBER_OBJECTS; // populated automatically by processDataORLIB int NUMBER_CONSTRAINTS; // populated automatically by processDataORLIB int OPTIMUM; // populated automatically by processDataORLIB int * CAPACITIES; // populated automatically by processDataORLIB int ** CONSTRAINTS; // populated automatically by processDataORLIB int * VALUES; // populated automatically by processDataORLIB const int POPULATION = 10; // size of the population const int LOCAL_IMPROVEMENT = 10; // number of local improvements const double INITIAL_POPULATION_PROB = 0.9; // the probability with which the initial population is generated const int GENERATIONS = 100000; // number of generations to run the algorithm const int GREEDY_CROSSOVER = 1; // a constant to identify greedy crossover in adaptive crossover const int ONE_POINT_CROSSOVER = 0; // a constant to identify one point crossover in adaptive crossover const int NEGATIVE_FITNESS = -100; // the fitness of an invalid individual (violating constraints) const int SHUFFLE_TOLERANCE = 2; // the number of attempts to identify unique parents before shuffling double MUTATION_PROBABILITY; // populated in main() double MUTATION_INCREMENT; // populated in main() double SHUFFLE_PROBABILITY; // populated in main() double MAX_MUTATION_PROBABILITY; // populated in main() int runtimeCrossoverType; // populated by adaptive crossover to identify which crossover operation was performed const int MAX_UNIQUE_ITERATIONS = 10000; // maximum iterations to spend on finding objects in local improvement /* A list of objects sorted in non-increasing order of the lagrangian psuedo-utility ratio. Used by localImprovement() */ vector GREEDY_OBJECTS; /* Lagrangian Relaxation Paremeters */ const double INITIAL_LAMBDA = 0; // the initial value of lambdas const double INITIAL_INCREMENT = 0.01; // the value of the increment with which the lambdas are increased or decreased const double LAMBDA_TOLERANCE = 0.00000001; // the value of the increment at which the calculation terminates const int COUNT_TOLERANCE = 100; // termination criteria constant for the inner loop of calculateLagrangianMultipliers const int DIFF_TOLERANCE = 2; // termination criteria constant for the inner loop of calculateLagrangianMultipliers double * LAMBDAS; // the lagrangian multipliers - populated by calculateLagrangianMultipliers double * SOLUTION; // the lagrangian dual solution - used by calculateLagrangianMultipliers /* Generate a random number in [min, max] */ int generateRandomNumber(int min, int max) { int r; double randd = ((double)rand() / ((double)(RAND_MAX)+(double)(1))); r = (int) (randd * (double)(max-min+1)) + min; return r; } /* Generate a random number in [0, 1) */ double generateDoubleRandomNumber() { return ((double)rand() / ((double)(RAND_MAX)+(double)(1))); } /* Return a random crossover type */ int randomCrossover() { int cc = (int)((double)generateDoubleRandomNumber() * (double)2); return cc; } /* Calculate the weights of this knapsack that is passed in */ int * calculateWeights(int * knapsack) { int * weights = new int[NUMBER_CONSTRAINTS]; for(int i=0; i 0) { value+=SOLUTION[i]; } } return value; } /* Calculate the lagrangian dual solution */ int * calculateSolution() { double * solution = new double[NUMBER_OBJECTS]; int * knapsack = new int[NUMBER_OBJECTS]; for(int i=0; i= COUNT_TOLERANCE) { count = 0; break; } } else { count = 0; } prevValue = value; } flag = true; for(int i=0; i 0) { LAMBDAS[i]-=increment; if(LAMBDAS[i] < 0) { LAMBDAS[i] = 0; } } } else if(weights[i] > CAPACITIES[i]) { flag = false; LAMBDAS[i]+=increment; } } if(flag) { break; } } if(increment <= tolerance) { break; } increment/=2; } } /* A genome in the genetic algorithm */ class KNode { private: int * knapsack; int value; int * weights; public: int crossoverType; public: /* Constructor */ KNode() { knapsack = NULL; weights = NULL; } /* Constructor */ KNode(int * knap) { /* Make a deep copy of the passed in array */ knapsack = new int [NUMBER_OBJECTS]; for(int i=0; inodeFitness(); int bf = right.nodeFitness(); return af > bf; } bool operator > (const KNode & right) const { int af = this->nodeFitness(); int bf = right.nodeFitness(); return af < bf; } bool operator == (const KNode & right) const { int af = this->nodeFitness(); int bf = right.nodeFitness(); return af == bf; } bool operator != (const KNode & right) const { int af = this->nodeValue(); int bf = right.nodeValue(); return af != bf; } /* Verify that the object of this class is a valid knapsack */ void checkKNode() const { for(int i=0; iweights[i] > CAPACITIES[i]) { return true; } } return false; } /* The fitness of this object - This is different from the value. If the object violates any of the constraints, a negative value is returned */ int nodeFitness() const { if(violatesConstraints()) { return NEGATIVE_FITNESS; } return nodeValue(); } /* Return the value of this knapsack object */ int nodeValue() const { return value; } /* Replace the value at the passed in index to 1 */ void addOne(int index) { if(knapsack[index]==1) { return; } value+=VALUES[index]; for(int i=0; i generateRandomPopulation() { vector population; for(int i=0; i &population) { printf("\nShuffling...\n"); for(int i=0; i randomSelection(vector &population) { vector parents; int count = 0; KNode node1; KNode node2; while(1) { int rand1 = generateRandomNumber(0, POPULATION-1); int rand2 = generateRandomNumber(0, POPULATION-1); if(rand1 == rand2) continue; node1.~KNode(); node2.~KNode(); node1 = population[rand1]; node2 = population[rand2]; if(node1.nodeValue() != node2.nodeValue()) { parents.push_back(node1); parents.push_back(node2); break; } count++; if(count > SHUFFLE_TOLERANCE) { shufflePopulation(population); } } return parents; } /* One Point Crossover */ inline vector onePointCrossover(KNode &n1, KNode &n2) { int rand = generateRandomNumber(1, NUMBER_OBJECTS-1); int * knap1 = new int[NUMBER_OBJECTS]; int * knap2 = new int[NUMBER_OBJECTS]; int i = 0; for(i=0; i children; KNode node1(knap1); KNode node2(knap2); /* Try to add items till the knapsack node1 is filled */ for(int i=0; i o2.ratio) { return -1; } else { return 0; } } /* Greedy Crossover */ inline vector greedyCrossover(KNode &n1, KNode &n2) { vector vec; int i=0; int count = 0; for(i=0; i childd; childd.push_back(node); return childd; } /* Adaptive Crossover */ vector crossover(KNode &node1, KNode &node2) { int type = -1; if(node1.crossoverType == node2.crossoverType) { type = node1.crossoverType; runtimeCrossoverType = type; } else { type = randomCrossover(); runtimeCrossoverType = type; } if(type == ONE_POINT_CROSSOVER) { vector children = onePointCrossover(node1, node2); if(children[0].nodeFitness() > node1.nodeFitness()) { // successful crossover children[0].crossoverType = ONE_POINT_CROSSOVER; node1.crossoverType = ONE_POINT_CROSSOVER; } else { children[0].crossoverType = randomCrossover(); node1.crossoverType = randomCrossover(); } if(children[0].nodeFitness() > node2.nodeFitness()) { // successful crossover children[0].crossoverType = ONE_POINT_CROSSOVER; node2.crossoverType = ONE_POINT_CROSSOVER; } else { children[0].crossoverType = randomCrossover(); node2.crossoverType = randomCrossover(); } if(children[1].nodeFitness() > node1.nodeFitness()) { // successful crossover children[1].crossoverType = ONE_POINT_CROSSOVER; node1.crossoverType = ONE_POINT_CROSSOVER; } else { children[1].crossoverType = randomCrossover(); node1.crossoverType = randomCrossover(); } if(children[1].nodeFitness() > node2.nodeFitness()) { // successful crossover children[1].crossoverType = ONE_POINT_CROSSOVER; node2.crossoverType = ONE_POINT_CROSSOVER; } else { children[1].crossoverType = randomCrossover(); node2.crossoverType = randomCrossover(); } return children; } else if(type == GREEDY_CROSSOVER) { vector children = greedyCrossover(node1, node2); if(children[0].nodeFitness() > node1.nodeFitness()) { // successful crossover children[0].crossoverType = GREEDY_CROSSOVER; node1.crossoverType = GREEDY_CROSSOVER; } else { children[0].crossoverType = randomCrossover(); node1.crossoverType = randomCrossover(); } if(children[0].nodeFitness() > node2.nodeFitness()) { // successful crossover children[0].crossoverType = GREEDY_CROSSOVER; node2.crossoverType = GREEDY_CROSSOVER; } else { children[0].crossoverType = randomCrossover(); node2.crossoverType = randomCrossover(); } return children; } else { // Should never happen printf("Could Not Identify Crossover Type %d", type); exit(1); } return NULL; } /* Try to randomly improve upon the passed in KNode, around the greedy neighborhood */ inline void localImprovement(KNode &n) { if(n.violatesConstraints()) return; KNode best(n); KNode node(n); KNode prev(n); for(int i=0; i MAX_UNIQUE_ITERATIONS) { break; } if(node.getValueOfIndex(rand) == 1) { node.resetBit(rand); count++; } if(count == 5) break; } for(int i=0; i best.nodeFitness()) { best.~KNode(); best = node; prev.~KNode(); prev = node; } else { node.~KNode(); node = prev; } } KNode ret(best.getKnapsack()); n.~KNode(); n = ret; } /* Greedy Algorithm - Creates a vector of objects arranged in non-increasing order of the psuedu utility ratio. This vector is used by the local improvement function */ void greedyAlgorithm(void) { for(int i=0; i population; population.reserve(POPULATION); population = generateRandomPopulation(); int i=0; // sort(population.begin(), population.end()); /* Create a global vector GREEDY_OBJECTS for use in the local improvement procedure */ greedyAlgorithm(); /* Declaration of variables used for adaptive mutation rate change */ KNode gBest; KNode prevBest; for(i=0; i newPopulation; newPopulation.reserve(POPULATION); int n = 0; int count = POPULATION; sort(population.begin(), population.end()); /* Elitism - The best individual of the previous population is copied to the next generation */ newPopulation.push_back(population[0].clone()); // should this be population[0].clone()? n++; gBest.~KNode(); gBest = population[0]; printf("%d:%d\n", i, gBest.nodeFitness()); if(gBest.nodeFitness() == OPTIMUM) { printf("Optimum Found - %d\n", newPopulation[0].nodeFitness()); break; } if(i==0) { prevBest = gBest; } else { /* Update mutation probabilities */ if(prevBest.nodeFitness() == gBest.nodeFitness()) { if(MUTATION_PROBABILITY < MAX_MUTATION_PROBABILITY) { MUTATION_PROBABILITY+=MUTATION_INCREMENT; } } else { MUTATION_PROBABILITY = MUTATION_INCREMENT; } } prevBest.~KNode(); prevBest = gBest; count--; for(int m=0; m &parents = randomSelection(population); /* Call adaptive crossover */ vector &children = crossover(parents[0], parents[1]); /* Mutation and Local Improvement */ if(runtimeCrossoverType == GREEDY_CROSSOVER) { m+=1; if(children[0].nodeFitness() <= parents[0].nodeFitness() && children[0].nodeFitness() <= parents[1].nodeFitness()) { mutate(children[0],MUTATION_PROBABILITY); } localImprovement(children[0]); newPopulation.push_back(children[0]); n++; } else if(runtimeCrossoverType == ONE_POINT_CROSSOVER) { m+=2; if(children[0].nodeFitness() <= parents[0].nodeFitness() && children[0].nodeFitness() <= parents[1].nodeFitness()) { mutate(children[0],MUTATION_PROBABILITY); } if(children[1].nodeFitness() <= parents[0].nodeFitness() && children[1].nodeFitness() <= parents[1].nodeFitness()) { mutate(children[1], MUTATION_PROBABILITY); } localImprovement(children[0]); newPopulation.push_back(children[0]); n++; localImprovement(children[1]); newPopulation.push_back(children[1]); n++; } else { // Should Never Happen printf("Unknown runtime Crossover Type"); exit(1); } } /* Generational Replacement */ population.swap(newPopulation); newPopulation.erase(newPopulation.begin(), newPopulation.end()); } population.erase(population.begin(), population.end()); GREEDY_OBJECTS.erase(GREEDY_OBJECTS.begin(), GREEDY_OBJECTS.end()); delete [] CAPACITIES; delete [] VALUES; delete [] SOLUTION; delete [] LAMBDAS; for(int i=0; i