Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
knapsack_test.cc
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
31
37# include <gtest/gtest.h>
38
39# include <algorithm>
40# include <cstdint>
41# include <limits>
42# include <random>
43
44# include <Knapsack.H>
45
46using namespace Aleph;
47
49
50namespace
51{
52 int selected_value(const Array<Item> & items,
53 const Array<size_t> & selected)
54 {
55 int total = 0;
56 for (size_t i = 0; i < selected.size(); ++i)
57 total += items[selected[i]].value;
58 return total;
59 }
60
61 int selected_weight(const Array<Item> & items,
62 const Array<size_t> & selected)
63 {
64 int total = 0;
65 for (size_t i = 0; i < selected.size(); ++i)
66 total += items[selected[i]].weight;
67 return total;
68 }
69
70 int brute_force_knapsack_01(const Array<Item> & items, const int capacity)
71 {
72 const size_t n = items.size();
73 const uint64_t total_masks = uint64_t(1) << n;
74 int best = 0;
75
76 for (uint64_t mask = 0; mask < total_masks; ++mask)
77 {
78 int w = 0, v = 0;
79 for (size_t i = 0; i < n; ++i)
80 if (mask & (uint64_t(1) << i))
81 {
82 w += items[i].weight;
83 v += items[i].value;
84 }
86 best = v;
87 }
88
89 return best;
90 }
91
92 int brute_force_bounded(const Array<Item> & items, const Array<size_t> & counts,
93 const int capacity, const size_t i = 0)
94 {
95 if (i == items.size())
96 return 0;
97
98 int best = brute_force_bounded(items, counts, capacity, i + 1);
99 const int wi = items[i].weight;
100 const int vi = items[i].value;
101
102 if (wi == 0)
103 {
104 if (vi > 0 and counts[i] > 0)
105 best = std::max(best,
106 static_cast<int>(counts[i]) * vi
107 + brute_force_bounded(items, counts, capacity, i + 1));
108 return best;
109 }
110
111 for (size_t take = 1; take <= counts[i]; ++take)
112 {
113 const int weight = static_cast<int>(take) * wi;
114 if (weight > capacity)
115 break;
116 best = std::max(best,
117 static_cast<int>(take) * vi
119 capacity - weight, i + 1));
120 }
121
122 return best;
123 }
124
125 int brute_force_unbounded(const Array<Item> & items,
126 const int capacity, const size_t i = 0)
127 {
128 if (i == items.size())
129 return 0;
130
131 int best = brute_force_unbounded(items, capacity, i + 1);
132 const int wi = items[i].weight;
133 const int vi = items[i].value;
134
135 if (wi <= 0)
136 return best;
137
138 for (int take = 1; take * wi <= capacity; ++take)
139 best = std::max(best,
140 take * vi + brute_force_unbounded(items,
141 capacity - take * wi,
142 i + 1));
143
144 return best;
145 }
146} // namespace
147
148
150{
151 Array<Item> items;
152 auto r = knapsack_01(items, 10);
153 EXPECT_EQ(r.optimal_value, 0);
154 EXPECT_EQ(r.selected_items.size(), 0u);
155}
156
158{
159 Array<Item> items = {{5, 10}, {3, 7}};
160 auto r = knapsack_01(items, 0);
161 EXPECT_EQ(r.optimal_value, 0);
162}
163
165{
166 // Items: (weight, value): (2,3), (3,4), (4,5), (5,6)
167 // Capacity = 8
168 // Best: items 1,2,3 (w=3+4+5=12 too heavy), items 0,1,2 (w=2+3+4=9 too heavy)
169 // items 0,2 (w=2+4=6, v=3+5=8), items 1,3 (w=3+5=8, v=4+6=10)
170 Array<Item> items = {{2, 3}, {3, 4}, {4, 5}, {5, 6}};
171 auto r = knapsack_01(items, 8);
172 EXPECT_EQ(r.optimal_value, 10);
173
174 // Verify selected items have correct total weight and value
175 int total_w = 0, total_v = 0;
176 for (size_t k = 0; k < r.selected_items.size(); ++k)
177 {
178 const auto & it = items[r.selected_items[k]];
179 total_w += it.weight;
180 total_v += it.value;
181 }
182 EXPECT_EQ(total_v, r.optimal_value);
183 EXPECT_LE(total_w, 8);
184}
185
187{
188 Array<Item> items = {{1, 10}, {1, 20}, {1, 30}};
189 auto r = knapsack_01(items, 100);
190 EXPECT_EQ(r.optimal_value, 60);
191 EXPECT_EQ(r.selected_items.size(), 3u);
192}
193
195{
196 Array<Item> items = {{2, 3}, {3, 4}, {4, 5}, {5, 6}};
197 EXPECT_EQ(knapsack_01_value(items, 8), 10);
198}
199
201{
202 Array<Item> items = {{1, 1}};
203 EXPECT_THROW(knapsack_01(items, -1), std::domain_error);
204 EXPECT_THROW(knapsack_01_value(items, -1), std::domain_error);
205}
206
208{
209 Array<Item> items = {{0, 5}, {0, 2}, {2, 7}};
210 const auto r = knapsack_01(items, 0);
211 EXPECT_EQ(r.optimal_value, 7);
212 EXPECT_EQ(selected_weight(items, r.selected_items), 0);
213 EXPECT_EQ(selected_value(items, r.selected_items), 7);
214 EXPECT_EQ(knapsack_01_value(items, 0), 7);
215}
216
218{
219 Array<Item> bad = {{-1, 10}, {2, 3}};
220 Array<size_t> counts = {1, 1};
221
222 EXPECT_THROW(knapsack_01(bad, 10), std::domain_error);
223 EXPECT_THROW(knapsack_01_value(bad, 10), std::domain_error);
224 EXPECT_THROW(knapsack_unbounded(bad, 10), std::domain_error);
225 EXPECT_THROW(knapsack_bounded(bad, counts, 10), std::domain_error);
226}
227
229{
230 // Items: (weight, value): (1,1), (3,4), (4,5)
231 // Capacity = 7
232 // Best unbounded: 2 * item1 (w=6,v=8) + 1 * item0 (w=1,v=1) = v=9
233 // Or: 1 * item2 (w=4,v=5) + 1 * item1 (w=3,v=4) = v=9
234 Array<Item> items = {{1, 1}, {3, 4}, {4, 5}};
235 auto r = knapsack_unbounded(items, 7);
236 EXPECT_EQ(r.optimal_value, 9);
237
238 // Verify weight
239 int total_w = 0;
240 for (size_t k = 0; k < r.selected_items.size(); ++k)
241 total_w += items[r.selected_items[k]].weight;
242 EXPECT_LE(total_w, 7);
243}
244
246{
247 Array<Item> items = {{0, 1}, {3, 4}};
248 EXPECT_THROW(knapsack_unbounded(items, 7), std::domain_error);
249}
250
252{
253 Array<Item> items;
254 auto r = knapsack_unbounded(items, 10);
255 EXPECT_EQ(r.optimal_value, 0);
256}
257
259{
260 // Item 0: w=2, v=3, count=2
261 // Item 1: w=3, v=5, count=1
262 // Capacity = 7
263 // Best: 2 * item0 (w=4,v=6) + 1 * item1 (w=3,v=5) = w=7, v=11
264 Array<Item> items = {{2, 3}, {3, 5}};
265 Array<size_t> counts = {2, 1};
266 auto r = knapsack_bounded(items, counts, 7);
267 EXPECT_EQ(r.optimal_value, 11);
268
269 // Verify weight and count constraints
270 int total_w = 0, total_v = 0;
272 for (size_t i = 0; i < items.size(); ++i)
273 used(i) = 0;
274
275 for (size_t k = 0; k < r.selected_items.size(); ++k)
276 {
277 const size_t idx = r.selected_items[k];
278 total_w += items[idx].weight;
279 total_v += items[idx].value;
280 used(idx)++;
281 }
282 EXPECT_EQ(total_v, r.optimal_value);
283 EXPECT_LE(total_w, 7);
284 for (size_t i = 0; i < items.size(); ++i)
285 EXPECT_LE(used(i), counts[i]);
286}
287
289{
290 Array<Item> items = {{1, 1}};
291 Array<size_t> counts = {1, 2};
292 EXPECT_THROW(knapsack_bounded(items, counts, 10), std::invalid_argument);
293}
294
296{
297 std::mt19937 rng(123456);
298 for (int trial = 0; trial < 80; ++trial)
299 {
300 const size_t n = 1 + rng() % 10;
301 const int capacity = static_cast<int>(rng() % 14);
302 Array<Item> items;
303 items.reserve(n);
304
305 for (size_t i = 0; i < n; ++i)
306 {
307 const int w = static_cast<int>(rng() % 7); // includes zero
308 const int v = static_cast<int>(rng() % 19) - 4; // may be negative
309 items.append(Item{w, v});
310 }
311
312 const auto r = knapsack_01(items, capacity);
313 const int brute = brute_force_knapsack_01(items, capacity);
314 const int value_only = knapsack_01_value(items, capacity);
315
316 EXPECT_EQ(r.optimal_value, brute);
318 EXPECT_EQ(selected_value(items, r.selected_items), r.optimal_value);
319 EXPECT_LE(selected_weight(items, r.selected_items), capacity);
320 }
321}
322
324{
325 std::mt19937 rng(424242);
326 for (int trial = 0; trial < 60; ++trial)
327 {
328 const size_t n = 1 + rng() % 7;
329 const int capacity = 1 + static_cast<int>(rng() % 16);
330 Array<Item> items;
331 items.reserve(n);
332
333 for (size_t i = 0; i < n; ++i)
334 {
335 const int w = 1 + static_cast<int>(rng() % 6); // strictly positive
336 const int v = static_cast<int>(rng() % 16) - 3;
337 items.append(Item{w, v});
338 }
339
340 const auto r = knapsack_unbounded(items, capacity);
341 const int brute = brute_force_unbounded(items, capacity);
342 EXPECT_EQ(r.optimal_value, brute);
343 EXPECT_EQ(selected_value(items, r.selected_items), r.optimal_value);
344 EXPECT_LE(selected_weight(items, r.selected_items), capacity);
345 }
346}
347
349{
350 std::mt19937 rng(777);
351 for (int trial = 0; trial < 60; ++trial)
352 {
353 const size_t n = 1 + rng() % 8;
354 const int capacity = static_cast<int>(rng() % 15);
355 Array<Item> items;
357 items.reserve(n);
358 counts.reserve(n);
359
360 for (size_t i = 0; i < n; ++i)
361 {
362 const int w = static_cast<int>(rng() % 5); // includes zero
363 const int v = static_cast<int>(rng() % 17) - 2;
364 const size_t c = static_cast<size_t>(rng() % 4);
365 items.append(Item{w, v});
366 counts.append(c);
367 }
368
369 const auto r = knapsack_bounded(items, counts, capacity);
370 const int brute = brute_force_bounded(items, counts, capacity);
371 EXPECT_EQ(r.optimal_value, brute);
372 EXPECT_EQ(selected_value(items, r.selected_items), r.optimal_value);
373 EXPECT_LE(selected_weight(items, r.selected_items), capacity);
374
376 for (size_t i = 0; i < n; ++i)
377 used(i) = 0;
378 for (size_t i = 0; i < r.selected_items.size(); ++i)
379 ++used(r.selected_items[i]);
380 for (size_t i = 0; i < n; ++i)
381 EXPECT_LE(used(i), counts[i]);
382 }
383}
Classical knapsack problem variants (0/1, unbounded, bounded).
long double w
Definition btreepic.C:153
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:139
static Array create(size_t n)
Create an array with n logical elements.
Definition tpl_array.H:194
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Definition tpl_array.H:351
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:245
void reserve(size_t cap)
Reserves cap cells into the array.
Definition tpl_array.H:315
#define TEST(name)
static mt19937 rng
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
V knapsack_01_value(const Array< Knapsack_Item< W, V > > &items, W capacity)
0/1 Knapsack — value only (space-optimized).
Definition Knapsack.H:199
Knapsack_Result< V > knapsack_01(const Array< Knapsack_Item< W, V > > &items, W capacity)
0/1 Knapsack with item reconstruction.
Definition Knapsack.H:131
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
Knapsack_Result< V > knapsack_bounded(const Array< Knapsack_Item< W, V > > &items, const Array< size_t > &counts, W capacity)
Bounded Knapsack with reconstruction.
Definition Knapsack.H:317
Knapsack_Result< V > knapsack_unbounded(const Array< Knapsack_Item< W, V > > &items, W capacity)
Unbounded Knapsack with reconstruction.
Definition Knapsack.H:247
An item for knapsack problems.
Definition Knapsack.H:66
static int * k
gsl_rng * r