Data Structures, Algorithms, & Applications in Java
Chapter 21, Exercise 9
We shall employ strategy 2 so that the complexity of the
code remains O(2n).
We define a new data member iMax
which gives the largest
i for which we have to save the
x[i] value.
Whenever we find a better solution,
iMax
is reset to n indicating that
x[1:n] are to be saved.
When we backup from a node, we first determine if the
x[i] value is to be saved.
If it is to be saved, then the x[i] value
is saved and the value of
iMax is decreased by 1.
Since the objects are reordered by profit densities, it is necessary
for us to record the mapping between objects in the sorted order and
in the original order. For this purpose, we use an additional
array id;
id[i] gives the original index of the
ith object in sorted order.
The new code is given below. Changes from RecursiveBTKnapsack,
which is the code given in the text,
are shown in red.
public class RecursiveBTKnapsack2
{
// top-level nested class
private static class Element implements Comparable
{
// data members
int id; // object identifier
double profitDensity;
// constructor
private Element(int theID, double theProfitDensity)
{
id = theID;
profitDensity = theProfitDensity;
}
public int compareTo(Object x)
{
double xpd = ((Element) x).profitDensity;
if (profitDensity < xpd) return -1;
if (profitDensity == xpd) return 0;
return 1;
}
public boolean equals(Object x)
{return profitDensity == ((Element) x).profitDensity;}
}
// class data members
static double capacity;
static int numberOfObjects;
static double [] weight;
static double [] profit;
static double weightOfCurrentPacking;
static double profitFromCurrentPacking;
static double maxProfitSoFar;
static int [] id; // original id of object i
static int [] currentSolution;
static int [] bestPackingSoFar; // best solution so far
static int saveToIndex; // incremental saving of
// bestPackingSoFar[1:saveToIndex] yet to be done
/** param theProfit[1:theProfit.length] is array of object profits
* param theWeight[1:theProfit.length] is array of object weights
* param theCapacity is knapsack capacity
* param theBestPacking solution array
* return profit of best filling */
public static double knapsack(double [] theProfit, double [] theWeight,
double theCapacity, int [] theBestPacking)
{
// set class data members
capacity = theCapacity;
numberOfObjects = theProfit.length - 1;
weightOfCurrentPacking = 0.0;
profitFromCurrentPacking = 0.0;
maxProfitSoFar = 0.0;
// define an Element array for profit densities
Element [] q = new Element [numberOfObjects];
// set up densities in q[0:n-1]
for (int i = 1; i <= numberOfObjects; i++)
q[i - 1] = new Element(i, theProfit[i] / theWeight[i]);
// sort into increasing density order
MergeSort.mergeSort(q);
// initialize class members
profit = new double [numberOfObjects + 1];
weight = new double [numberOfObjects + 1];
id = new int [numberOfObjects + 1];
for (int i = 1; i <= numberOfObjects; i++)
{// profits and weights in decreasing density order
profit[i] = theProfit[q[numberOfObjects - i].id];
weight[i] = theWeight[q[numberOfObjects - i].id];
id[i] = q[numberOfObjects - i].id;
}
currentSolution = new int [numberOfObjects + 1];
bestPackingSoFar = theBestPacking;
saveToIndex = 0;
rKnap(1); // compute max profit
if (maxProfitSoFar == 0.0)
// no object fits
for (int i = 1; i <= numberOfObjects; i++)
theBestPacking[i] = 0;
return maxProfitSoFar;
}
/** actual method to find value of best filling */
private static void rKnap(int currentLevel)
{// search from a node at currentLevel
if (currentLevel > numberOfObjects)
{// at a leaf, need to save all values
saveToIndex = numberOfObjects;
maxProfitSoFar = profitFromCurrentPacking;
return;
}
// not at a leaf, check subtrees
if (weightOfCurrentPacking + weight[currentLevel] <= capacity)
{// try left subtree
currentSolution[currentLevel] = 1;
weightOfCurrentPacking += weight[currentLevel];
profitFromCurrentPacking += profit[currentLevel];
rKnap(currentLevel + 1);
if (saveToIndex == currentLevel)
{// must save currentSolution[currentLevel]
bestPackingSoFar[id[currentLevel]] = 1;
saveToIndex--;
}
weightOfCurrentPacking -= weight[currentLevel];
profitFromCurrentPacking -= profit[currentLevel];
}
if (profitBound(currentLevel + 1) > maxProfitSoFar)
{// try right subtree
currentSolution[currentLevel] = 0;
rKnap(currentLevel + 1);
if (saveToIndex == currentLevel)
{// must save currentSolution[currentLevel]
bestPackingSoFar[id[currentLevel]] = 0;
saveToIndex--;
}
}
}
/** bounding function
* return upper bound on value of best leaf in subtree */
private static double profitBound(int currentLevel)
{
double remainingCapacity = capacity - weightOfCurrentPacking;
double profitBound = profitFromCurrentPacking;
// fill remaining capacity in order of profit density
while (currentLevel <= numberOfObjects &&
weight[currentLevel] <= remainingCapacity)
{
remainingCapacity -= weight[currentLevel];
profitBound += profit[currentLevel];
currentLevel++;
}
// take fraction of next object
if (currentLevel <= numberOfObjects)
profitBound += profit[currentLevel] / weight[currentLevel]
* remainingCapacity;
return profitBound;
}
}