Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Knapsack.H
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
72#ifndef KNAPSACK_H
73#define KNAPSACK_H
74
75#include <algorithm>
76#include <concepts>
77#include <cstddef>
78#include <limits>
79#include <type_traits>
80#include <utility>
81
82#include <ah-errors.H>
83#include <tpl_array.H>
84
85namespace Aleph {
91template <typename W, typename V>
93{
96};
97
105template <typename V>
111
112namespace knapsack_detail {
113template <std::integral W>
114[[nodiscard]] inline size_t to_size_checked(const W value, const char *fn_name, const char *field_name)
115{
116 if constexpr (std::is_signed_v<W>)
117 ah_domain_error_if(value < W{}) << fn_name << ": " << field_name << " must be non-negative";
118
119 using UW = std::make_unsigned_t<W>;
120 const UW uvalue = static_cast<UW>(value);
121 if constexpr (sizeof(UW) > sizeof(size_t))
122 ah_out_of_range_error_if(uvalue > static_cast<UW>(std::numeric_limits<size_t>::max()))
123 << fn_name << ": " << field_name << " is too large for size_t";
124
125 return static_cast<size_t>(uvalue);
126}
127
128template <std::integral W, typename V>
130 const char *fn_name)
131{
132 Array<size_t> weights = Array<size_t>::create(items.size());
133 for (size_t i = 0; i < items.size(); ++i)
134 weights(i) = to_size_checked(items[i].weight, fn_name, "item weight");
135 return weights;
136}
137// Safe DP addition: clamps to numeric_limits<V>::max() for integral V to
138// prevent signed overflow UB and unsigned wrap-around.
139template <typename V>
140[[nodiscard]] inline V dp_add(V a, V b) noexcept
141{
142 if constexpr (std::is_integral_v<V>)
143 if (b > V{} and a > std::numeric_limits<V>::max() - b)
144 return std::numeric_limits<V>::max();
145 return a + b;
146}
147} // namespace knapsack_detail
148
165template <typename V, std::integral W>
167{
168 const size_t n = items.size();
169 const size_t C = knapsack_detail::to_size_checked(capacity, "knapsack_01", "capacity");
170 const Array<size_t> weights = knapsack_detail::extract_weights_checked(items, "knapsack_01");
171
172 if (n == 0)
174
175 // dp[i][w] = best value using items 0..i-1 with capacity w
176 Array<Array<V>> dp;
177 dp.reserve(n + 1);
178 for (size_t i = 0; i <= n; ++i)
179 {
181 for (size_t w = 0; w <= C; ++w)
182 row(w) = V{};
183 dp.append(std::move(row));
184 }
185
186 for (size_t i = 1; i <= n; ++i)
187 {
188 const size_t wi = weights[i - 1];
189 const V vi = items[i - 1].value;
190 for (size_t w = 0; w <= C; ++w)
191 {
192 dp[i][w] = dp[i - 1][w];
193 if (wi <= w)
194 {
195 const V candidate = knapsack_detail::dp_add(dp[i - 1][w - wi], vi);
196 if (candidate > dp[i - 1][w])
197 dp[i][w] = candidate;
198 }
199 }
200 }
201
202 // reconstruct
204 size_t w = C;
205 for (size_t i = n; i > 0; --i)
206 if (dp[i][w] != dp[i - 1][w])
207 {
208 sel.append(i - 1);
209 w -= weights[i - 1];
210 }
211
212 // reverse to ascending order
213 Array<size_t> selected;
214 selected.reserve(sel.size());
215 for (size_t k = sel.size(); k > 0; --k)
216 selected.append(sel[k - 1]);
217
218 return Knapsack_Result<V>{dp[n][C], std::move(selected)};
219}
220
236template <typename V, std::integral W>
238{
239 const size_t n = items.size();
240 const size_t C = knapsack_detail::to_size_checked(capacity, "knapsack_01_value", "capacity");
241 const Array<size_t> weights = knapsack_detail::extract_weights_checked(items, "knapsack_01_value");
242
243 if (n == 0)
244 return V{};
245
246 Array<V> dp = Array<V>::create(C + 1);
247 for (size_t w = 0; w <= C; ++w)
248 dp(w) = V{};
249
250 for (size_t i = 0; i < n; ++i)
251 {
252 const size_t wi = weights[i];
253 const V vi = items[i].value;
254 // w != static_cast<size_t>(-1) guards against unsigned wrap-around when wi == 0
255 for (size_t w = C; w >= wi and w != static_cast<size_t>(-1); --w)
256 {
257 const V candidate = knapsack_detail::dp_add(dp[w - wi], vi);
258 if (candidate > dp[w])
259 dp(w) = candidate;
260 }
261 }
262
263 return dp[C];
264}
265
285template <typename V, std::integral W>
287 W capacity)
288{
289 const size_t n = items.size();
290 const size_t C = knapsack_detail::to_size_checked(capacity, "knapsack_unbounded", "capacity");
291 const Array<size_t> weights
292 = knapsack_detail::extract_weights_checked(items, "knapsack_unbounded");
293
294 if (n == 0)
296
297 for (size_t i = 0; i < n; ++i)
298 ah_domain_error_if(weights[i] == 0 and items[i].value > V{})
299 << "knapsack_unbounded: zero-weight item with positive value " << "leads to unbounded optimum";
300
301 Array<V> dp = Array<V>::create(C + 1);
302 // choice[w] = which item was last added at capacity w
304 constexpr size_t NONE = std::numeric_limits<size_t>::max();
305 for (size_t w = 0; w <= C; ++w)
306 {
307 dp(w) = V{};
308 choice(w) = NONE;
309 }
310
311 for (size_t w = 1; w <= C; ++w)
312 for (size_t i = 0; i < n; ++i)
313 if (const size_t wi = weights[i]; wi <= w)
314 {
315 const V candidate = knapsack_detail::dp_add(dp[w - wi], items[i].value);
316 if (candidate > dp[w])
317 {
318 dp(w) = candidate;
319 choice(w) = i;
320 }
321 }
322
323 // reconstruct
325 size_t w = C;
326 while (w > 0 and choice[w] != NONE)
327 {
328 const size_t idx = choice[w];
329 sel.append(idx);
330 w -= weights[idx];
331 }
332
333 return Knapsack_Result<V>{dp[C], std::move(sel)};
334}
335
356template <typename V, std::integral W>
358 const Array<size_t> &counts,
359 W capacity)
360{
361 ah_invalid_argument_if(items.size() != counts.size())
362 << "knapsack_bounded: items and counts must have same size";
363
364 const size_t n = items.size();
365 const size_t C = knapsack_detail::to_size_checked(capacity, "knapsack_bounded", "capacity");
366 const Array<size_t> weights = knapsack_detail::extract_weights_checked(items, "knapsack_bounded");
367
368 if (n == 0)
370
371 // Binary decomposition: for item i with count c, create groups
372 // of 1, 2, 4, ..., 2^k, remainder
374 Array<size_t> origin; // maps expanded index -> original index
375 Array<size_t> multiplier; // how many copies this group represents
376
377 for (size_t i = 0; i < n; ++i)
378 {
379 const size_t wi = weights[i];
380 size_t rem = counts[i];
381 size_t k = 1;
382 while (rem > 0)
383 {
384 const size_t take = std::min(k, rem);
385
386 bool value_ok;
387 if constexpr (std::is_integral_v<V>)
388 {
389 if (items[i].value == V{0})
390 value_ok = true;
391 else if (items[i].value > V{0})
392 {
393 // Positive: check take * value <= V::max() using UV to avoid
394 // truncation if V is wider than size_t.
395 using UV = std::make_unsigned_t<V>;
396 const UV value_uv = static_cast<UV>(items[i].value);
397 const UV maxV_uv = static_cast<UV>(std::numeric_limits<V>::max());
398 value_ok = (static_cast<UV>(take) <= maxV_uv / value_uv);
399 }
400 else
401 {
402 // Negative: check take * |value| <= |V::min()|
403 using UV = std::make_unsigned_t<V>;
404 const UV abs_value = static_cast<UV>(-(items[i].value + V{1})) + UV{1};
405 // Casting V::min() to its unsigned type yields |V::min()| in two's complement.
406 const UV abs_min = static_cast<UV>(std::numeric_limits<V>::min());
407 value_ok = (static_cast<UV>(take) <= abs_min / abs_value);
408 }
409 }
410 else if constexpr (std::numeric_limits<V>::is_specialized)
411 {
412 const V maxV = std::numeric_limits<V>::max();
413 if (items[i].value == V{0})
414 value_ok = true;
415 else
416 {
417 // Use |value| so negative values are handled correctly.
418 const V absV = items[i].value > V{0} ? items[i].value : -items[i].value;
419 value_ok = (static_cast<V>(take) <= maxV / absV);
420 }
421 }
422 else
423 value_ok = true;
424
425 if ((wi == 0 or take <= C / wi) and value_ok)
426 {
427 const V val = static_cast<V>(take) * items[i].value;
428 expanded.append(Knapsack_Item<W, V>{static_cast<W>(wi * take), val});
429 origin.append(i);
430 multiplier.append(take);
431 }
432
433 rem -= take;
434 if (rem == 0)
435 break;
436
437 if (k > std::numeric_limits<size_t>::max() / 2)
438 k = rem;
439 else
440 k *= 2;
441 }
442 }
443
444 // Solve as 0/1
445 auto result = knapsack_01(expanded, capacity);
446
447 // Map back to original indices
449 for (size_t k = 0; k < result.selected_items.size(); ++k)
450 {
451 const size_t ei = result.selected_items[k];
452 const size_t orig = origin[ei];
453 const size_t mult = multiplier[ei];
454 for (size_t j = 0; j < mult; ++j)
455 sel.append(orig);
456 }
457
458 return Knapsack_Result<V>{result.optimal_value, std::move(sel)};
459}
460} // namespace Aleph
461
462#endif // KNAPSACK_H
Exception handling system with formatted messages for Aleph-w.
#define ah_out_of_range_error_if(C)
Throws std::out_of_range if condition holds.
Definition ah-errors.H:584
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:527
#define ah_invalid_argument_if(C)
Throws std::invalid_argument if condition holds.
Definition ah-errors.H:644
long double w
Definition btreepic.C:153
size_t size_t int32_t value
Definition ca-c-api.h:116
size_t row
Definition ca-c-api.h:115
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:138
static Array create(size_t n)
Create an array with n logical elements.
Definition tpl_array.H:196
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Definition tpl_array.H:365
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:250
void reserve(size_t cap)
Reserves cap cells into the array.
Definition tpl_array.H:320
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
V dp_add(V a, V b) noexcept
Definition Knapsack.H:140
Array< size_t > extract_weights_checked(const Array< Knapsack_Item< W, V > > &items, const char *fn_name)
Definition Knapsack.H:129
size_t to_size_checked(const W value, const char *fn_name, const char *field_name)
Definition Knapsack.H:114
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
V knapsack_01_value(const Array< Knapsack_Item< W, V > > &items, W capacity)
Solve the 0/1 Knapsack problem (value only, space-optimized).
Definition Knapsack.H:237
and
Check uniqueness with explicit hash + equality functors.
Knapsack_Result< V > knapsack_01(const Array< Knapsack_Item< W, V > > &items, W capacity)
Solve the 0/1 Knapsack problem with item reconstruction.
Definition Knapsack.H:166
Knapsack_Result< V > knapsack_bounded(const Array< Knapsack_Item< W, V > > &items, const Array< size_t > &counts, W capacity)
Solve the Bounded Knapsack problem with reconstruction.
Definition Knapsack.H:357
Knapsack_Result< V > knapsack_unbounded(const Array< Knapsack_Item< W, V > > &items, W capacity)
Solve the Unbounded Knapsack problem with reconstruction.
Definition Knapsack.H:286
An item for knapsack problems.
Definition Knapsack.H:93
V value
Value (or profit) of the item.
Definition Knapsack.H:95
W weight
Weight (or cost) of the item.
Definition Knapsack.H:94
Result of a knapsack computation.
Definition Knapsack.H:107
Array< size_t > selected_items
Indices (0-based) of items selected for the optimum.
Definition Knapsack.H:109
V optimal_value
Maximum total value achieved.
Definition Knapsack.H:108
size_t V
static int * k
Dynamic array container with automatic resizing.