Skip to content

Commit 3d92c39

Browse files
authored
Switch to file-scoped namespaces (#430)
1 parent 231167f commit 3d92c39

36 files changed

+1720
-1770
lines changed

Algorithms/Knapsack/BranchAndBoundKnapsackSolver.cs

+132-133
Original file line numberDiff line numberDiff line change
@@ -2,164 +2,163 @@
22
using System.Collections.Generic;
33
using System.Linq;
44

5-
namespace Algorithms.Knapsack
5+
namespace Algorithms.Knapsack;
6+
7+
/// <summary>
8+
/// Branch and bound Knapsack solver.
9+
/// </summary>
10+
/// <typeparam name="T">Type of items in knapsack.</typeparam>
11+
public class BranchAndBoundKnapsackSolver<T>
612
{
713
/// <summary>
8-
/// Branch and bound Knapsack solver.
14+
/// Returns the knapsack containing the items that maximize value while not exceeding weight capacity.
15+
/// Construct a tree structure with total number of items + 1 levels, each node have two child nodes,
16+
/// starting with a dummy item root, each following levels are associated with 1 items, construct the
17+
/// tree in breadth first order to identify the optimal item set.
918
/// </summary>
10-
/// <typeparam name="T">Type of items in knapsack.</typeparam>
11-
public class BranchAndBoundKnapsackSolver<T>
19+
/// <param name="items">All items to choose from.</param>
20+
/// <param name="capacity">The maximum weight capacity of the knapsack to be filled.</param>
21+
/// <param name="weightSelector">
22+
/// A function that returns the value of the specified item
23+
/// from the <paramref name="items">items</paramref> list.
24+
/// </param>
25+
/// <param name="valueSelector">
26+
/// A function that returns the weight of the specified item
27+
/// from the <paramref name="items">items</paramref> list.
28+
/// </param>
29+
/// <returns>
30+
/// The array of items that provides the maximum value of the
31+
/// knapsack without exceeding the specified weight <paramref name="capacity">capacity</paramref>.
32+
/// </returns>
33+
public T[] Solve(T[] items, int capacity, Func<T, int> weightSelector, Func<T, double> valueSelector)
1234
{
13-
/// <summary>
14-
/// Returns the knapsack containing the items that maximize value while not exceeding weight capacity.
15-
/// Construct a tree structure with total number of items + 1 levels, each node have two child nodes,
16-
/// starting with a dummy item root, each following levels are associated with 1 items, construct the
17-
/// tree in breadth first order to identify the optimal item set.
18-
/// </summary>
19-
/// <param name="items">All items to choose from.</param>
20-
/// <param name="capacity">The maximum weight capacity of the knapsack to be filled.</param>
21-
/// <param name="weightSelector">
22-
/// A function that returns the value of the specified item
23-
/// from the <paramref name="items">items</paramref> list.
24-
/// </param>
25-
/// <param name="valueSelector">
26-
/// A function that returns the weight of the specified item
27-
/// from the <paramref name="items">items</paramref> list.
28-
/// </param>
29-
/// <returns>
30-
/// The array of items that provides the maximum value of the
31-
/// knapsack without exceeding the specified weight <paramref name="capacity">capacity</paramref>.
32-
/// </returns>
33-
public T[] Solve(T[] items, int capacity, Func<T, int> weightSelector, Func<T, double> valueSelector)
34-
{
35-
// This is required for greedy approach in upper bound calculation to work.
36-
items = items.OrderBy(i => valueSelector(i) / weightSelector(i)).ToArray();
35+
// This is required for greedy approach in upper bound calculation to work.
36+
items = items.OrderBy(i => valueSelector(i) / weightSelector(i)).ToArray();
3737

38-
// nodesQueue --> used to construct tree in breadth first order
39-
Queue<BranchAndBoundNode> nodesQueue = new();
38+
// nodesQueue --> used to construct tree in breadth first order
39+
Queue<BranchAndBoundNode> nodesQueue = new();
4040

41-
// maxCumulativeValue --> maximum value while not exceeding weight capacity.
42-
var maxCumulativeValue = 0.0;
41+
// maxCumulativeValue --> maximum value while not exceeding weight capacity.
42+
var maxCumulativeValue = 0.0;
4343

44-
// starting node, associated with a temporary created dummy item
45-
BranchAndBoundNode root = new(level: -1, taken: false);
44+
// starting node, associated with a temporary created dummy item
45+
BranchAndBoundNode root = new(level: -1, taken: false);
4646

47-
// lastNodeOfOptimalPat --> last item in the optimal item sets identified by this algorithm
48-
BranchAndBoundNode lastNodeOfOptimalPath = root;
47+
// lastNodeOfOptimalPat --> last item in the optimal item sets identified by this algorithm
48+
BranchAndBoundNode lastNodeOfOptimalPath = root;
4949

50-
nodesQueue.Enqueue(root);
50+
nodesQueue.Enqueue(root);
51+
52+
while (nodesQueue.Count != 0)
53+
{
54+
// parent --> parent node which represents the previous item, may or may not be taken into the knapsack
55+
BranchAndBoundNode parent = nodesQueue.Dequeue();
5156

52-
while (nodesQueue.Count != 0)
57+
// IF it is the last level, branching cannot be performed
58+
if (parent.Level == items.Length - 1)
5359
{
54-
// parent --> parent node which represents the previous item, may or may not be taken into the knapsack
55-
BranchAndBoundNode parent = nodesQueue.Dequeue();
56-
57-
// IF it is the last level, branching cannot be performed
58-
if (parent.Level == items.Length - 1)
59-
{
60-
continue;
61-
}
62-
63-
// create a child node where the associated item is taken into the knapsack
64-
var left = new BranchAndBoundNode(parent.Level + 1, true, parent);
65-
66-
// create a child node where the associated item is not taken into the knapsack
67-
var right = new BranchAndBoundNode(parent.Level + 1, false, parent);
68-
69-
// Since the associated item on current level is taken for the first node,
70-
// set the cumulative weight of first node to cumulative weight of parent node + weight of the associated item,
71-
// set the cumulative value of first node to cumulative value of parent node + value of current level's item.
72-
left.CumulativeWeight = parent.CumulativeWeight + weightSelector(items[left.Level]);
73-
left.CumulativeValue = parent.CumulativeValue + valueSelector(items[left.Level]);
74-
right.CumulativeWeight = parent.CumulativeWeight;
75-
right.CumulativeValue = parent.CumulativeValue;
76-
77-
// IF cumulative weight is smaller than the weight capacity of the knapsack AND
78-
// current cumulative value is larger then the current maxCumulativeValue, update the maxCumulativeValue
79-
if (left.CumulativeWeight <= capacity && left.CumulativeValue > maxCumulativeValue)
80-
{
81-
maxCumulativeValue = left.CumulativeValue;
82-
lastNodeOfOptimalPath = left;
83-
}
84-
85-
left.UpperBound = ComputeUpperBound(left, items, capacity, weightSelector, valueSelector);
86-
right.UpperBound = ComputeUpperBound(right, items, capacity, weightSelector, valueSelector);
87-
88-
// IF upperBound of this node is larger than maxCumulativeValue,
89-
// the current path is still possible to reach or surpass the maximum value,
90-
// add current node to nodesQueue so that nodes below it can be further explored
91-
if (left.UpperBound > maxCumulativeValue && left.CumulativeWeight < capacity)
92-
{
93-
nodesQueue.Enqueue(left);
94-
}
95-
96-
// Cumulative weight is the same as for parent node and < capacity
97-
if (right.UpperBound > maxCumulativeValue)
98-
{
99-
nodesQueue.Enqueue(right);
100-
}
60+
continue;
10161
}
10262

103-
return GetItemsFromPath(items, lastNodeOfOptimalPath);
104-
}
63+
// create a child node where the associated item is taken into the knapsack
64+
var left = new BranchAndBoundNode(parent.Level + 1, true, parent);
10565

106-
// determine items taken based on the path
107-
private static T[] GetItemsFromPath(T[] items, BranchAndBoundNode lastNodeOfPath)
108-
{
109-
List<T> takenItems = new();
66+
// create a child node where the associated item is not taken into the knapsack
67+
var right = new BranchAndBoundNode(parent.Level + 1, false, parent);
11068

111-
// only bogus initial node has no parent
112-
for (var current = lastNodeOfPath; current.Parent is not null; current = current.Parent)
69+
// Since the associated item on current level is taken for the first node,
70+
// set the cumulative weight of first node to cumulative weight of parent node + weight of the associated item,
71+
// set the cumulative value of first node to cumulative value of parent node + value of current level's item.
72+
left.CumulativeWeight = parent.CumulativeWeight + weightSelector(items[left.Level]);
73+
left.CumulativeValue = parent.CumulativeValue + valueSelector(items[left.Level]);
74+
right.CumulativeWeight = parent.CumulativeWeight;
75+
right.CumulativeValue = parent.CumulativeValue;
76+
77+
// IF cumulative weight is smaller than the weight capacity of the knapsack AND
78+
// current cumulative value is larger then the current maxCumulativeValue, update the maxCumulativeValue
79+
if (left.CumulativeWeight <= capacity && left.CumulativeValue > maxCumulativeValue)
11380
{
114-
if(current.IsTaken)
115-
{
116-
takenItems.Add(items[current.Level]);
117-
}
81+
maxCumulativeValue = left.CumulativeValue;
82+
lastNodeOfOptimalPath = left;
11883
}
11984

120-
return takenItems.ToArray();
85+
left.UpperBound = ComputeUpperBound(left, items, capacity, weightSelector, valueSelector);
86+
right.UpperBound = ComputeUpperBound(right, items, capacity, weightSelector, valueSelector);
87+
88+
// IF upperBound of this node is larger than maxCumulativeValue,
89+
// the current path is still possible to reach or surpass the maximum value,
90+
// add current node to nodesQueue so that nodes below it can be further explored
91+
if (left.UpperBound > maxCumulativeValue && left.CumulativeWeight < capacity)
92+
{
93+
nodesQueue.Enqueue(left);
94+
}
95+
96+
// Cumulative weight is the same as for parent node and < capacity
97+
if (right.UpperBound > maxCumulativeValue)
98+
{
99+
nodesQueue.Enqueue(right);
100+
}
121101
}
122102

123-
/// <summary>
124-
/// Returns the upper bound value of a given node.
125-
/// </summary>
126-
/// <param name="aNode">The given node.</param>
127-
/// <param name="items">All items to choose from.</param>
128-
/// <param name="capacity">The maximum weight capacity of the knapsack to be filled.</param>
129-
/// <param name="weightSelector">
130-
/// A function that returns the value of the specified item
131-
/// from the <paramref name="items">items</paramref> list.
132-
/// </param>
133-
/// <param name="valueSelector">
134-
/// A function that returns the weight of the specified item
135-
/// from the <paramref name="items">items</paramref> list.
136-
/// </param>
137-
/// <returns>
138-
/// upper bound value of the given <paramref name="aNode">node</paramref>.
139-
/// </returns>
140-
private static double ComputeUpperBound(BranchAndBoundNode aNode, T[] items, int capacity, Func<T, int> weightSelector, Func<T, double> valueSelector)
103+
return GetItemsFromPath(items, lastNodeOfOptimalPath);
104+
}
105+
106+
// determine items taken based on the path
107+
private static T[] GetItemsFromPath(T[] items, BranchAndBoundNode lastNodeOfPath)
108+
{
109+
List<T> takenItems = new();
110+
111+
// only bogus initial node has no parent
112+
for (var current = lastNodeOfPath; current.Parent is not null; current = current.Parent)
141113
{
142-
var upperBound = aNode.CumulativeValue;
143-
var availableWeight = capacity - aNode.CumulativeWeight;
144-
var nextLevel = aNode.Level + 1;
114+
if(current.IsTaken)
115+
{
116+
takenItems.Add(items[current.Level]);
117+
}
118+
}
119+
120+
return takenItems.ToArray();
121+
}
145122

146-
while (availableWeight > 0 && nextLevel < items.Length)
123+
/// <summary>
124+
/// Returns the upper bound value of a given node.
125+
/// </summary>
126+
/// <param name="aNode">The given node.</param>
127+
/// <param name="items">All items to choose from.</param>
128+
/// <param name="capacity">The maximum weight capacity of the knapsack to be filled.</param>
129+
/// <param name="weightSelector">
130+
/// A function that returns the value of the specified item
131+
/// from the <paramref name="items">items</paramref> list.
132+
/// </param>
133+
/// <param name="valueSelector">
134+
/// A function that returns the weight of the specified item
135+
/// from the <paramref name="items">items</paramref> list.
136+
/// </param>
137+
/// <returns>
138+
/// upper bound value of the given <paramref name="aNode">node</paramref>.
139+
/// </returns>
140+
private static double ComputeUpperBound(BranchAndBoundNode aNode, T[] items, int capacity, Func<T, int> weightSelector, Func<T, double> valueSelector)
141+
{
142+
var upperBound = aNode.CumulativeValue;
143+
var availableWeight = capacity - aNode.CumulativeWeight;
144+
var nextLevel = aNode.Level + 1;
145+
146+
while (availableWeight > 0 && nextLevel < items.Length)
147+
{
148+
if (weightSelector(items[nextLevel]) <= availableWeight)
147149
{
148-
if (weightSelector(items[nextLevel]) <= availableWeight)
149-
{
150-
upperBound += valueSelector(items[nextLevel]);
151-
availableWeight -= weightSelector(items[nextLevel]);
152-
}
153-
else
154-
{
155-
upperBound += valueSelector(items[nextLevel]) / weightSelector(items[nextLevel]) * availableWeight;
156-
availableWeight = 0;
157-
}
158-
159-
nextLevel++;
150+
upperBound += valueSelector(items[nextLevel]);
151+
availableWeight -= weightSelector(items[nextLevel]);
152+
}
153+
else
154+
{
155+
upperBound += valueSelector(items[nextLevel]) / weightSelector(items[nextLevel]) * availableWeight;
156+
availableWeight = 0;
160157
}
161158

162-
return upperBound;
159+
nextLevel++;
163160
}
161+
162+
return upperBound;
164163
}
165164
}
+20-21
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,29 @@
1-
namespace Algorithms.Knapsack
1+
namespace Algorithms.Knapsack;
2+
3+
public class BranchAndBoundNode
24
{
3-
public class BranchAndBoundNode
4-
{
5-
// isTaken --> true = the item where index = level is taken, vice versa
6-
public bool IsTaken { get; }
5+
// isTaken --> true = the item where index = level is taken, vice versa
6+
public bool IsTaken { get; }
77

8-
// cumulativeWeight --> um of weight of item associated in each nodes starting from root to this node (only item that is taken)
9-
public int CumulativeWeight { get; set; }
8+
// cumulativeWeight --> um of weight of item associated in each nodes starting from root to this node (only item that is taken)
9+
public int CumulativeWeight { get; set; }
1010

11-
// cumulativeValue --> sum of value of item associated in each nodes starting from root to this node (only item that is taken)
12-
public double CumulativeValue { get; set; }
11+
// cumulativeValue --> sum of value of item associated in each nodes starting from root to this node (only item that is taken)
12+
public double CumulativeValue { get; set; }
1313

14-
// upperBound --> largest possible value after taking/not taking the item associated to this node (fractional)
15-
public double UpperBound { get; set; }
14+
// upperBound --> largest possible value after taking/not taking the item associated to this node (fractional)
15+
public double UpperBound { get; set; }
1616

17-
// level --> level of the node in the tree structure
18-
public int Level { get; }
17+
// level --> level of the node in the tree structure
18+
public int Level { get; }
1919

20-
// parent node
21-
public BranchAndBoundNode? Parent { get; }
20+
// parent node
21+
public BranchAndBoundNode? Parent { get; }
2222

23-
public BranchAndBoundNode(int level, bool taken, BranchAndBoundNode? parent = null)
24-
{
25-
Level = level;
26-
IsTaken = taken;
27-
Parent = parent;
28-
}
23+
public BranchAndBoundNode(int level, bool taken, BranchAndBoundNode? parent = null)
24+
{
25+
Level = level;
26+
IsTaken = taken;
27+
Parent = parent;
2928
}
3029
}

0 commit comments

Comments
 (0)