Skip to content

Branch and bound #230

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Aug 4, 2021
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Update BranchAndBoundKnapsackSolver.cs
  • Loading branch information
threelittle87 committed Jul 30, 2021
commit 5ab708ac8f7825c67a5f0a9880a95cf2c1394336
98 changes: 50 additions & 48 deletions Algorithms/Knapsack/BranchAndBoundKnapsackSolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ public T[] Solve(T[] items, int capacity, Func<T, int> weightSelector, Func<T, d
// starting node, associated with a temporary created dummy item
BranchAndBoundNode root = new BranchAndBoundNode();

root.SetLevel(-1);
root.SetCumulativeWeight(0);
root.SetCumulativeValue(0);
root.Level = -1;
root.CumulativeWeight = 0;
root.CumulativeValue = 0;

nodesQueue.Enqueue(root);

Expand All @@ -61,68 +61,70 @@ public T[] Solve(T[] items, int capacity, Func<T, int> weightSelector, Func<T, d
{
parent = nodesQueue.Dequeue();

// IF there are still item could be at the lower level
if (parent.GetLevel() < items.Length - 1)
// IF it is the last level
if (parent.Level > items.Length - 1)
{
// create a child node where the associated item is taken into the knapsack
nodes.Add(new BranchAndBoundNode(parent.GetLevel() + 1, true, parent));
continue;
}

// create a child node where the associated item is not taken into the knapsack
nodes.Add(new BranchAndBoundNode(parent.GetLevel() + 1, false, parent));
// create a child node where the associated item is taken into the knapsack
nodes.Add(new BranchAndBoundNode(parent.Level + 1, true, parent));

// Since the associated item on current level is taken for the first node,
// set the cumulative weight of first node to cumulative weight of parent node + weight of the associated item,
// set the cumulative value of first node to cumulative value of parent node + value of current level's item.
nodes[counter].SetCumulativeWeight(parent.GetCumulativeWeight() + weightSelector(items[nodes[counter].GetLevel()]));
nodes[counter].SetCumulativeValue(parent.GetCumulativeValue() + valueSelector(items[nodes[counter].GetLevel()]));
// create a child node where the associated item is not taken into the knapsack
nodes.Add(new BranchAndBoundNode(parent.Level + 1, false, parent));

// IF cumulative weight is smaller than the weight capacity of the knapsack AND
// current cumulative value is larger then the current maxCumulativeValue, update the maxCumulativeValue
if (nodes[counter].GetCumulativeWeight() <= capacity && nodes[counter].GetCumulativeValue() > maxCumulativeValue)
{
maxCumulativeValue = nodes[counter].GetCumulativeValue();
lastNodeOfOptimalPath = nodes[counter];
}
// Since the associated item on current level is taken for the first node,
// set the cumulative weight of first node to cumulative weight of parent node + weight of the associated item,
// set the cumulative value of first node to cumulative value of parent node + value of current level's item.
nodes[counter].CumulativeWeight = parent.CumulativeWeight + weightSelector(items[nodes[counter].Level]);
nodes[counter].CumulativeValue = parent.CumulativeValue + valueSelector(items[nodes[counter].Level]);

// find upperBound of this node
nodes[counter].SetUpperBound(ComputeUpperBound(nodes[counter], items, capacity, weightSelector, valueSelector));
// IF cumulative weight is smaller than the weight capacity of the knapsack AND
// current cumulative value is larger then the current maxCumulativeValue, update the maxCumulativeValue
if (nodes[counter].CumulativeWeight <= capacity && nodes[counter].CumulativeValue > maxCumulativeValue)
{
maxCumulativeValue = nodes[counter].CumulativeValue;
lastNodeOfOptimalPath = nodes[counter];
}

// IF upperBound of this node is larger than maxCumulativeValue,
// the current path is still possible to reach or surpass the maximum value,
// add current node to nodesQueue so that nodes can be further contructed below it
if (nodes[counter].GetUpperBound() > maxCumulativeValue && nodes[counter].GetCumulativeWeight() < capacity)
{
nodesQueue.Enqueue(nodes[counter]);
}
// find upperBound of this node
nodes[counter].UpperBound = ComputeUpperBound(nodes[counter], items, capacity, weightSelector, valueSelector);

// repeat everything for the second node but the node's level's item is not taken
counter++;
nodes[counter].SetParent(parent);
// IF upperBound of this node is larger than maxCumulativeValue,
// the current path is still possible to reach or surpass the maximum value,
// add current node to nodesQueue so that nodes below it can be further explored
if (nodes[counter].UpperBound > maxCumulativeValue && nodes[counter].CumulativeWeight < capacity)
{
nodesQueue.Enqueue(nodes[counter]);
}

nodes[counter].SetCumulativeWeight(parent.GetCumulativeWeight());
nodes[counter].SetCumulativeValue(parent.GetCumulativeValue());
// repeat everything for the second node but the node's level's item is not taken
counter++;
nodes[counter].Parent = parent;

nodes[counter].SetUpperBound(ComputeUpperBound(nodes[counter], items, capacity, weightSelector, valueSelector));
nodes[counter].CumulativeWeight = parent.CumulativeWeight;
nodes[counter].CumulativeValue = parent.CumulativeValue;

if (nodes[counter].GetUpperBound() > maxCumulativeValue)
{
nodesQueue.Enqueue(nodes[counter]);
}
nodes[counter].UpperBound = ComputeUpperBound(nodes[counter], items, capacity, weightSelector, valueSelector);

counter++;
if (nodes[counter].UpperBound > maxCumulativeValue)
{
nodesQueue.Enqueue(nodes[counter]);
}

counter++;
}

return GetOptimalItems(items, lastNodeOfOptimalPath);
return GetOptimalItems(items, lastNodeOfOptimalPath, weightSelector, valueSelector);
}

// determine items taken based on the path that gives maximum value
private static T[] GetOptimalItems(T[] items, BranchAndBoundNode lastNodeOfOptimalPath)
private static T[] GetOptimalItems(T[] items, BranchAndBoundNode lastNodeOfOptimalPath, Func<T, int> weightSelector, Func<T, double> valueSelector)
{
List<T> takenItems = new List<T>();

BranchAndBoundNode? currentNode = lastNodeOfOptimalPath;
int numberOfNodes = lastNodeOfOptimalPath.GetLevel();
int numberOfNodes = lastNodeOfOptimalPath.Level;

for (int i = numberOfNodes; i >= 0; i--)
{
Expand All @@ -135,7 +137,7 @@ private static T[] GetOptimalItems(T[] items, BranchAndBoundNode lastNodeOfOptim
}

// set currentNode to its parent to check if the parent is taken in the next iteration
currentNode = currentNode.GetParent();
currentNode = currentNode.Parent;
}
}

Expand All @@ -161,9 +163,9 @@ private static T[] GetOptimalItems(T[] items, BranchAndBoundNode lastNodeOfOptim
/// </returns>
private static double ComputeUpperBound(BranchAndBoundNode aNode, T[] items, int capacity, Func<T, int> weightSelector, Func<T, double> valueSelector)
{
double upperBound = aNode.GetCumulativeValue();
int availableWeight = capacity - aNode.GetCumulativeWeight();
int nextLevel = aNode.GetLevel() + 1;
double upperBound = aNode.CumulativeValue;
int availableWeight = capacity - aNode.CumulativeWeight;
int nextLevel = aNode.Level + 1;

while (availableWeight > 0 && nextLevel < items.Length)
{
Expand Down