Skip to content

Commit 0ce85ce

Browse files
committedApr 30, 2018
Add knapsack problem.
1 parent d20d0c8 commit 0ce85ce

File tree

5 files changed

+101
-33
lines changed

5 files changed

+101
-33
lines changed
 

‎README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
### Algorithms by Paradigm
8181

8282
* **Greedy**
83+
* [Unbound Knapsack Problem](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/knapsack-problem)
8384
* **Divide and Conquer**
8485
* [Euclidean Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/euclidean-algorithm) - calculate the Greatest Common Divisor (GCD)
8586
* [Permutations](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/permutations) (with and without repetitions)
@@ -95,7 +96,7 @@
9596
* [Longest Common Substring](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/string/longest-common-substring)
9697
* [Longest Increasing subsequence](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/longest-increasing-subsequence)
9798
* [Shortest Common Supersequence](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/shortest-common-supersequence)
98-
* [Knapsack Problem](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/knapsack-problem)
99+
* [0/1 Knapsack Problem](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/knapsack-problem)
99100
* Maximum subarray
100101
* Maximum sum path
101102
* Integer Partition

‎src/algorithms/sets/knapsack-problem/Knapsack.js

+46-11
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,9 @@ export default class Knapsack {
99
this.selectedItems = [];
1010
this.weightLimit = weightLimit;
1111
this.possibleItems = possibleItems;
12-
// We do two sorts because in case of equal weights but different values
13-
// we need to take the most valuable items first.
14-
this.sortPossibleItemsByValue();
15-
this.sortPossibleItemsByWeight();
1612
}
1713

1814
sortPossibleItemsByWeight() {
19-
// Sort possible items by their weight.
20-
// We need them to be sorted in order to solve knapsack problem using
21-
// Dynamic Programming approach.
2215
this.possibleItems = new MergeSort({
2316
/**
2417
* @var KnapsackItem itemA
@@ -35,9 +28,6 @@ export default class Knapsack {
3528
}
3629

3730
sortPossibleItemsByValue() {
38-
// Sort possible items by their weight.
39-
// We need them to be sorted in order to solve knapsack problem using
40-
// Dynamic Programming approach.
4131
this.possibleItems = new MergeSort({
4232
/**
4333
* @var KnapsackItem itemA
@@ -53,8 +43,30 @@ export default class Knapsack {
5343
}).sort(this.possibleItems);
5444
}
5545

56-
// Solve 0/1 knapsack problem using dynamic programming.
46+
sortPossibleItemsByValuePerWeightRatio() {
47+
this.possibleItems = new MergeSort({
48+
/**
49+
* @var KnapsackItem itemA
50+
* @var KnapsackItem itemB
51+
*/
52+
compareCallback: (itemA, itemB) => {
53+
if (itemA.valuePerWeightRatio === itemB.valuePerWeightRatio) {
54+
return 0;
55+
}
56+
57+
return itemA.valuePerWeightRatio > itemB.valuePerWeightRatio ? -1 : 1;
58+
},
59+
}).sort(this.possibleItems);
60+
}
61+
62+
// Solve 0/1 knapsack problem
63+
// Dynamic Programming approach.
5764
solveZeroOneKnapsackProblem() {
65+
// We do two sorts because in case of equal weights but different values
66+
// we need to take the most valuable items first.
67+
this.sortPossibleItemsByValue();
68+
this.sortPossibleItemsByWeight();
69+
5870
this.selectedItems = [];
5971

6072
// Create knapsack values matrix.
@@ -138,6 +150,29 @@ export default class Knapsack {
138150
}
139151
}
140152

153+
154+
// Solve unbounded knapsack problem.
155+
// Greedy approach.
156+
solveUnboundedKnapsackProblem() {
157+
this.sortPossibleItemsByValue();
158+
this.sortPossibleItemsByValuePerWeightRatio();
159+
160+
for (let itemIndex = 0; itemIndex < this.possibleItems.length; itemIndex += 1) {
161+
if (this.totalWeight < this.weightLimit) {
162+
const currentItem = this.possibleItems[itemIndex];
163+
164+
// Detect how much of current items we can push to knapsack.
165+
const availableWeight = this.weightLimit - this.totalWeight;
166+
const maxPossibleItemsCount = Math.floor(availableWeight / currentItem.weight);
167+
168+
if (maxPossibleItemsCount) {
169+
currentItem.quantity = maxPossibleItemsCount;
170+
this.selectedItems.push(currentItem);
171+
}
172+
}
173+
}
174+
}
175+
141176
get totalValue() {
142177
/** @var {KnapsackItem} item */
143178
return this.selectedItems.reduce((accumulator, item) => {

‎src/algorithms/sets/knapsack-problem/KnapsackItem.js

+6
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ export default class KnapsackItem {
2121
return this.weight * this.quantity;
2222
}
2323

24+
// This coefficient shows how valuable the 1 unit of weight is
25+
// for current item.
26+
get valuePerWeightRatio() {
27+
return this.value / this.weight;
28+
}
29+
2430
toString() {
2531
return `v${this.value} w${this.weight} x ${this.quantity}`;
2632
}

‎src/algorithms/sets/knapsack-problem/__test__/Knapsack.test.js

+23
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,27 @@ describe('Knapsack', () => {
8686
expect(knapsack.selectedItems[1].toString()).toBe('v5 w1 x 1');
8787
expect(knapsack.selectedItems[2].toString()).toBe('v7 w1 x 1');
8888
});
89+
90+
it('should solve unbound knapsack problem', () => {
91+
const possibleKnapsackItems = [
92+
new KnapsackItem({ value: 84, weight: 7 }), // v/w ratio is 12
93+
new KnapsackItem({ value: 5, weight: 2 }), // v/w ratio is 2.5
94+
new KnapsackItem({ value: 12, weight: 3 }), // v/w ratio is 4
95+
new KnapsackItem({ value: 10, weight: 1 }), // v/w ratio is 10
96+
new KnapsackItem({ value: 20, weight: 2 }), // v/w ratio is 10
97+
];
98+
99+
const maxKnapsackWeight = 17;
100+
101+
const knapsack = new Knapsack(possibleKnapsackItems, maxKnapsackWeight);
102+
103+
knapsack.solveUnboundedKnapsackProblem();
104+
105+
expect(knapsack.totalValue).toBe(84 + 84 + 20 + 10);
106+
expect(knapsack.totalWeight).toBe(17);
107+
expect(knapsack.selectedItems.length).toBe(3);
108+
expect(knapsack.selectedItems[0].toString()).toBe('v84 w7 x 2');
109+
expect(knapsack.selectedItems[1].toString()).toBe('v20 w2 x 1');
110+
expect(knapsack.selectedItems[2].toString()).toBe('v10 w1 x 1');
111+
});
89112
});

‎src/algorithms/sets/knapsack-problem/__test__/KnapsackItem.test.js

+24-21
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,34 @@ import KnapsackItem from '../KnapsackItem';
22

33
describe('KnapsackItem', () => {
44
it('should create knapsack item and count its total weight and value', () => {
5-
const item1 = new KnapsackItem({ value: 3, weight: 2 });
5+
const knapsackItem = new KnapsackItem({ value: 3, weight: 2 });
66

7-
expect(item1.value).toBe(3);
8-
expect(item1.weight).toBe(2);
9-
expect(item1.quantity).toBe(1);
10-
expect(item1.toString()).toBe('v3 w2 x 1');
11-
expect(item1.totalValue).toBe(3);
12-
expect(item1.totalWeight).toBe(2);
7+
expect(knapsackItem.value).toBe(3);
8+
expect(knapsackItem.weight).toBe(2);
9+
expect(knapsackItem.quantity).toBe(1);
10+
expect(knapsackItem.valuePerWeightRatio).toBe(1.5);
11+
expect(knapsackItem.toString()).toBe('v3 w2 x 1');
12+
expect(knapsackItem.totalValue).toBe(3);
13+
expect(knapsackItem.totalWeight).toBe(2);
1314

14-
item1.quantity = 0;
15+
knapsackItem.quantity = 0;
1516

16-
expect(item1.value).toBe(3);
17-
expect(item1.weight).toBe(2);
18-
expect(item1.quantity).toBe(0);
19-
expect(item1.toString()).toBe('v3 w2 x 0');
20-
expect(item1.totalValue).toBe(0);
21-
expect(item1.totalWeight).toBe(0);
17+
expect(knapsackItem.value).toBe(3);
18+
expect(knapsackItem.weight).toBe(2);
19+
expect(knapsackItem.quantity).toBe(0);
20+
expect(knapsackItem.valuePerWeightRatio).toBe(1.5);
21+
expect(knapsackItem.toString()).toBe('v3 w2 x 0');
22+
expect(knapsackItem.totalValue).toBe(0);
23+
expect(knapsackItem.totalWeight).toBe(0);
2224

23-
item1.quantity = 2;
25+
knapsackItem.quantity = 2;
2426

25-
expect(item1.value).toBe(3);
26-
expect(item1.weight).toBe(2);
27-
expect(item1.quantity).toBe(2);
28-
expect(item1.toString()).toBe('v3 w2 x 2');
29-
expect(item1.totalValue).toBe(6);
30-
expect(item1.totalWeight).toBe(4);
27+
expect(knapsackItem.value).toBe(3);
28+
expect(knapsackItem.weight).toBe(2);
29+
expect(knapsackItem.quantity).toBe(2);
30+
expect(knapsackItem.valuePerWeightRatio).toBe(1.5);
31+
expect(knapsackItem.toString()).toBe('v3 w2 x 2');
32+
expect(knapsackItem.totalValue).toBe(6);
33+
expect(knapsackItem.totalWeight).toBe(4);
3134
});
3235
});

0 commit comments

Comments
 (0)
Please sign in to comment.