/** * Calculates the value/cost ratio for each object * And stores the ratios in a vector * TODO sort values to improve runtime * @param k the knapsack to use */ void calculateValueCostRatios(knapsack &k) { for (int i = 0; i < k.getNumObjects(); i++) { valueCostRatio.insert(valueCostRatio.begin() + i, (k.getValue(i) / (double)k.getCost(i))); } }
knapsack::knapsack(const knapsack &k) // Knapsack copy constructor { numObjects = k.getNumObjects(); costBound = k.getCostBound(); value.resize(numObjects); cost.resize(numObjects); index.resize(numObjects); selected.resize(numObjects); for (int i = 0; i < numObjects; i++) { setValue(i,k.getValue(i)); setCost(i,k.getCost(i)); setIndex(i,k.getIndex(i)); if (k.isSelected(i)) select(i); else unSelect(i); } setNum(k.getNum()); currentValue = k.getCurrentValue(); currentCost = k.getCurrentCost(); }
knapsack greedyKnapsack(vector<pair<double,int>> sortedSack, knapsack sack) { int sackIndex; // outer for loop is iterating through sortedSack ratios // look for highest ratio item for (int i = 0; i < sack.getNumObjects(); i++) { // use second value of sorted sack pair as index sackIndex = sortedSack.at(i).second; //get ratio for specific sack(sackIndex) double ratioKnap; double valKnap = sack.getValue(sackIndex); double costKnap = sack.getCost(sackIndex); ratioKnap = valKnap / costKnap; if (sack.getCost() + sack.getCost(sackIndex) <= sack.getCostLimit()) { sack.select(sackIndex); } } for (int k = 0; k < sack.getNumObjects(); k++) { cout<<sack.isSelected(k)<<endl; } return sack; }
/** * Uses a greedy algorithm to find the best solution * @param k the knapsack to use * @param t the time limit */ void greedySolve(knapsack &k) { calculateValueCostRatios(k); while (k.getCost() < k.getCostLimit()) { checkTimeLimit(); int best = getBestObject(k); if (best == -1) break; k.select(best); valueCostRatio[best] = 0; } }
/** * Selects the current best object to select * @param k the knapsack to use */ int getBestObject(knapsack &k) { int best = -1; for (int i = 0; i < k.getNumObjects(); i++) { if (best == -1 || valueCostRatio[i] > valueCostRatio[best]) { if (valueCostRatio[i] > 0 && k.getCost(i) <= (k.getCostLimit() - k.getCost())) { best = i; } } } return best; }
double greedyKnapsack(knapsack &k){ MaxHeap<float, int>* heap = new MaxHeap<float, int>(&compare); for(int counter = 0; counter < k.getNumObjects(); counter++){ double key = (double) k.getValue(counter) / k.getCost(counter); heap->add(key, counter); } while(! heap->empty()){ int next = heap->extractMaxHeapMaximum(); if(k.getCost(next) + k.getCurrentCost() <= k.getCostBound()){ k.select(next); } } return k.getCurrentValue(); }
void exhaustiveKnapsack(knapsack sack, int timeLimit) { // Get start time to be used in while loop time_t startTime; time(&startTime); // a while loop that expires when time limit is complete by checking difference of // start time and current time at every loop while (true) { // Get current time and stop when greater than input time limit time_t newTime; time(&newTime); if (difftime(newTime, startTime) > timeLimit) { cout << "Time limit expired"; break; } // Put main code here // Update current weight in sack every iteration of while loop int itemsSelected = 0; int currentWeight = 0; for (int i = 0; i < sack.getNumObjects(); i++) { itemsSelected++; currentWeight = sack.getCost(i) + currentWeight; } // Create random index to put into sack srand(time(NULL)); int randNum = rand() % sack.getNumObjects(); // if item isn't selected, and there is enough space, then select item if ((sack.isSelected(randNum) == false) && sack.getCost(randNum) + currentWeight <= sack.getCostLimit()) { sack.select(randNum); } // Need an exit case where there is no more items that can fit in the sack else if (sack.getCost(randNum) + currentWeight > sack.getCostLimit()) { // need to record sack weight and value and items when this happens (and there are // no more items that can legally be added) } } }
// sort all items in order of value/weight ratio // and store the ratios in the index at which they exist in knapsack vector<pair<double, int>> sortItems(knapsack sack) { vector<pair<double, int>> ratios; for (int i = 0; i < sack.getNumObjects(); i++) { //cout << "sack.getValue(" << i << ")" << " = " << sack.getValue(i) << endl; //cout << "sack.getValue(" << i << ")" << " = " << sack.getCost(i) << endl; double ratio; double val = sack.getValue(i); double cost = sack.getCost(i); ratio = val / cost; //cout << "Ratio: " << ratio; //cout << "Pushing " << ratio << " to index " << i << endl; pair <double, int> ratInd; // ratio + index ratInd.first = ratio; ratInd.second = i; ratios.push_back(ratInd); //cout << endl << endl << endl; } // this preserves the original ratios vector just in case (may remove it later) vector<pair<double,int>> sortedRatios = ratios; sort(sortedRatios.begin(), sortedRatios.end()); reverse(sortedRatios.begin(), sortedRatios.end()); /* for (int i = 0; i < ratios.size(); i++) { //cout << "Ratio: " << sortedRatios.at(i) << " at index " << i << endl; //cout << "" << sortedRatios.at(i) << endl; }*/ cin.get(); return sortedRatios; }
void branchAndBound(knapsack &k, int maxTime) // Implement a Branch and Bound search for an optimal solution. // Searches for the best decision for objects n and greater, as long // as more than maxTime seconds have not elapsed. Tries // to keep or not keep object n, and then iterates to n+1. Stores // the best solution found so far in bestSolution. { deque<knapsack> problem; clock_t startTime = clock(); knapsack bestSolution(k); knapsack pessimisticBound(k); greedyKnapsack(pessimisticBound); float bestValue = pessimisticBound.getCurrentValue(); // Initially, decisions have not been made about any objects, // so set num = 0. k.setNum(0); // Add the empty knapsack subproblem to the list problem.push_back(k); // Branch and Bound search goes here while(! problem.empty() && ((float)clock()-(float)startTime)/CLOCKS_PER_SEC < maxTime){ knapsack next = problem.front();problem.pop_front(); if(next.getCurrentValue() > bestSolution.getCurrentValue()){ bestSolution = next; if(bestSolution.getCurrentValue() > bestValue) bestValue = bestSolution.getCurrentValue(); } knapsack k1(next); k1.select(k1.getNum()); k1.setNum(k1.getNum() + 1); if(k1.getCurrentCost() <= k1.getCostBound() && k1.bound() > bestValue && k1.getNum() < k1.getNumObjects()){ problem.push_back(k1); } knapsack k2(next); k2.setNum(k2.getNum() + 1); if(k2.bound() > bestValue && k2.getNum() < k2.getNumObjects()){ problem.push_back(k2); } } cout << "Best value found: " << bestSolution.getCurrentValue() << endl; cout << bestSolution; }
/** * @param k knapsack to find a solution for * @return new knapsack with a random solution */ knapsack randomSolution(knapsack &k) { int i = 0; while (checkValid(k) && i < k.getNumObjects()) { int random = rand() % 2; if (random == 1 && (k.getCost() + k.getCost(i)) < k.getCostLimit()) k.select(i); else k.unSelect(i); i++; } return k; }
void printSolution(knapsack &k, string filename) // Prints out the solution. { ofstream myfile; myfile.open (filename.c_str()); myfile << "------------------------------------------------" << endl; myfile << "Total value: " << k.getCurrentValue() << endl; myfile << "Total cost: " << k.getCurrentCost() << endl << endl; // Print out objects in the solution for (int i = 0; i < k.getNumObjects(); i++) if (k.isSelected(i)) myfile << i << " " << k.getValue(i) << " " << k.getCost(i) << endl; myfile << endl; myfile.close(); }
/** * Finds a local optimum and returns the result * * @param k knapsack to find local optimum for * @param int 2-opt or 3-opt * @return new knapsack with a local opt solution */ knapsack localOptimum(knapsack &k, int opt) { knapsack original(k); deque<knapsack> problem; knapsack bestFound = knapsack(k); try { for (int l = 0; l < k.getNumObjects() - 1; l++) { k = knapsack(original); if (!k.isSelected(l)) continue; k.unSelect(l); for (int m = l; m < k.getNumObjects() - 1; m++) { if (!k.isSelected(m)) continue; k.unSelect(m); if (opt == 3) { for (int n = m; n < k.getNumObjects() - 1; n++) { if (!k.isSelected(n)) continue; k.unSelect(n); problem.push_front(knapsack(k)); } } else problem.push_front(knapsack(k)); } } while(problem.size() > 0) { checkTimeLimit(); knapsack current = problem.front(); problem.pop_front(); deque<knapsack> solution; solution.push_front(knapsack(current)); int currentIndex = 0; while(solution.size() > 0) { knapsack currentSolution = solution.front(); solution.pop_front(); if (currentSolution.getValue() > bestFound.getValue()) { bestFound = knapsack(currentSolution); } if (currentIndex < currentSolution.getNumObjects() - 1) { if (!currentSolution.isSelected(currentIndex) && (currentSolution.getCost() + currentSolution.getCost(currentIndex)) < currentSolution.getCostLimit()) { currentSolution.select(currentIndex); solution.push_front(knapsack(currentSolution)); } currentIndex++; } } } return bestFound; } catch (baseException &ex) { cout << ex.what() << endl; return bestFound; } } //end localOptimum
/** * Check if a knapsack contains a valid selection * @param k the knapsack instance to check */ bool checkValid(knapsack &k) { return (k.getCost() < k.getCostLimit()); }
void branchAndBound(knapsack &k, int maxTime) // Implement a Branch and Bound search for an optimal solution. // Searches for the best decision for objects n and greater, as long // as more than maxTime seconds have not elapsed. Tries // to keep or not keep object n, and then iterates to n+1. Stores // the best solution found so far in bestSolution. { clock_t endTime, startTime = clock(); deque<knapsack> problem; deque<knapsack>::iterator itr; int number,bestValue=0; knapsack bestSolution; double time=0; cout<<"Branching and Bounding"<<endl; // Initially, decisions have not been made about any objects, // so set num = 0. k.setNum(0); // Add the empty knapsack subproblem to the list problem.push_front(k); // Branch and Bound search goes here while (!(problem.empty()) && (time<=maxTime)) //for each object in the deque { k = problem.back(); // current instance is now the last object in the deque while((k.getNum()!=k.getNumObjects()) && (k.bound()> bestValue) && (time<=maxTime)) /*if we haven't considered all the objects left and the bound of the current instance is not worse than our best-so-far solution and we are not out of time*/ { //delete the last object from the deque because we are currently solving it problem.pop_back(); number=k.getNum(); k.setNum(number+1); //we are now considering the next object if(k.getCostBound()-k.getCurrentCost()>k.getCost(number)) { problem.push_back(k); //push the 'don't select' instacne before the take instance k.select(number); } problem.push_back(k); //push the remaining instance endTime=clock();//update current time time=((double)(endTime-startTime))/CLOCKS_PER_SEC; } if (k.getCurrentValue() > bestValue){// replace best values if current ones are better bestValue = k.getCurrentValue(); bestSolution = k; } problem.pop_back(); //delete the last object from the deque since it is fathomed } cout << "Best value found: " << bestValue << endl; cout << bestSolution; k.outFile(bestSolution); }