Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Subset_Sum.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
31
46# ifndef SUBSET_SUM_H
47# define SUBSET_SUM_H
48
49# include <algorithm>
50# include <concepts>
51# include <cstddef>
52# include <cstdint>
53# include <limits>
54# include <type_traits>
55# include <utility>
56
57# include <ah-errors.H>
58# include <tpl_array.H>
59# include <tpl_sort_utils.H>
60
61namespace Aleph
62{
64 template <typename T>
70
71 namespace subset_sum_detail
72 {
73 template <std::integral T>
74 [[nodiscard]] inline size_t
75 to_size_checked(const T value, const char *fn_name,
76 const char *field_name)
77 {
78 if constexpr (std::is_signed_v<T>)
79 ah_domain_error_if(value < T{})
80 << fn_name << ": " << field_name << " must be non-negative";
81
82 using UT = std::make_unsigned_t<T>;
83 const UT uvalue = static_cast<UT>(value);
84 if constexpr (sizeof(UT) > sizeof(size_t))
86 static_cast<UT>(
87 std::numeric_limits<size_t>::max()))
88 << fn_name << ": " << field_name << " is too large for size_t";
89
90 return static_cast<size_t>(uvalue);
91 }
92
93 template <std::integral T>
94 [[nodiscard]] inline Array<size_t>
95 extract_values_checked(const Array<T> & values, const char *fn_name)
96 {
98 for (size_t i = 0; i < values.size(); ++i)
99 converted(i) = to_size_checked(values[i], fn_name, "value");
100 return converted;
101 }
102
103 // Enumerate all subset sums of arr[start..start+len-1]
104 // Returns array of pairs (sum, bitmask relative to start)
105 template <typename T>
107 enumerate_sums(const Array<T> & arr, size_t start, size_t len)
108 {
109 ah_out_of_range_error_if(len >= std::numeric_limits<uint64_t>::digits)
110 << "subset_sum_mitm: each half must have fewer than 64 elements";
111
112 const uint64_t count = static_cast<uint64_t>(1) << len;
113 ah_out_of_range_error_if(count > std::numeric_limits<size_t>::max())
114 << "subset_sum_mitm: subset count does not fit size_t";
115
117 result.reserve(static_cast<size_t>(count));
118
119 for (uint64_t mask = 0; mask < count; ++mask)
120 {
121 long long s = 0;
122 for (size_t j = 0; j < len; ++j)
123 if (mask & (static_cast<uint64_t>(1) << j))
124 s += static_cast<long long>(arr[start + j]);
125 result.append(std::make_pair(s, mask));
126 }
127
128 return result;
129 }
130 } // namespace subset_sum_detail
131
132
145 template <std::integral T>
147 subset_sum(const Array<T> & values, T target)
148 {
149 const size_t n = values.size();
150 const size_t tgt
151 = subset_sum_detail::to_size_checked(target, "subset_sum", "target");
153 = subset_sum_detail::extract_values_checked(values, "subset_sum");
154
155 if (tgt == 0)
156 return Subset_Sum_Result<T>{true, Array<size_t>()};
157
158 if (n == 0)
159 return Subset_Sum_Result<T>{false, Array<size_t>()};
160
161 // dp[i][s] = can we achieve sum s using items 0..i-1?
162 // Use bit-per-row for space efficiency? No, we need reconstruction.
164 dp.reserve(n + 1);
165 for (size_t i = 0; i <= n; ++i)
166 {
167 Array<char> row = Array<char>::create(tgt + 1);
168 for (size_t s = 0; s <= tgt; ++s)
169 row(s) = 0;
170 dp.append(std::move(row));
171 }
172
173 dp[0][0] = 1;
174
175 for (size_t i = 1; i <= n; ++i)
176 {
177 const size_t vi = weights[i - 1];
178 for (size_t s = 0; s <= tgt; ++s)
179 {
180 dp[i][s] = dp[i - 1][s];
181 if (not dp[i][s] and vi <= s and dp[i - 1][s - vi])
182 dp[i][s] = 1;
183 }
184 }
185
186 if (not dp[n][tgt])
187 return Subset_Sum_Result<T>{false, Array<size_t>()};
188
189 // reconstruct
191 size_t s = tgt;
192 for (size_t i = n; i > 0 and s > 0; --i)
193 if (dp[i][s] and not dp[i - 1][s])
194 {
195 sel.append(i - 1);
196 s -= weights[i - 1];
197 }
198
199 // reverse
200 Array<size_t> selected;
201 selected.reserve(sel.size());
202 for (size_t k = sel.size(); k > 0; --k)
203 selected.append(sel[k - 1]);
204
205 return Subset_Sum_Result<T>{true, std::move(selected)};
206 }
207
208
221 template <std::integral T>
222 [[nodiscard]] bool
223 subset_sum_exists(const Array<T> & values, T target)
224 {
225 const size_t n = values.size();
226 const size_t tgt = subset_sum_detail::to_size_checked(
227 target, "subset_sum_exists", "target");
229 values, "subset_sum_exists");
230
231 if (tgt == 0)
232 return true;
233 if (n == 0)
234 return false;
235
236 Array<char> dp = Array<char>::create(tgt + 1);
237 for (size_t s = 0; s <= tgt; ++s)
238 dp(s) = 0;
239 dp(0) = 1;
240
241 for (size_t i = 0; i < n; ++i)
242 {
243 const size_t vi = weights[i];
244 for (size_t s = tgt; s >= vi and s != static_cast<size_t>(-1); --s)
245 if (dp[s - vi])
246 dp(s) = 1;
247 }
248
249 return dp[tgt];
250 }
251
252
265 template <std::integral T>
266 [[nodiscard]] size_t
267 subset_sum_count(const Array<T> & values, T target)
268 {
269 const size_t n = values.size();
270 const size_t tgt = subset_sum_detail::to_size_checked(target, "subset_sum_count", "target");
272 values, "subset_sum_count");
273
275 for (size_t s = 0; s <= tgt; ++s)
276 dp(s) = 0;
277 dp(0) = 1;
278
279 for (size_t i = 0; i < n; ++i)
280 {
281 const size_t vi = weights[i];
282 for (size_t s = tgt; s >= vi and s != static_cast<size_t>(-1); --s)
283 if (const size_t count = dp[s - vi]; count > 0)
284 {
285 if (dp(s) > std::numeric_limits<size_t>::max() - count)
286 dp(s) = std::numeric_limits<size_t>::max();
287 else
288 dp(s) += count;
289 }
290 }
291
292 return dp[tgt];
293 }
294
311 template <std::integral T>
313 subset_sum_mitm(const Array<T> & values, T target)
314 {
315 const size_t n = values.size();
316
317 if (n == 0)
318 return Subset_Sum_Result<T>{target == T{}, Array<size_t>()};
319
320 const size_t half1 = n / 2;
321 const size_t half2 = n - half1;
322
325
326 // sort sums2 by sum value
327 introsort(sums2, [](const auto & a, const auto & b)
328 {
329 return a.first < b.first;
330 });
331
332 const auto target_ll = static_cast<long long>(target);
333 for (size_t i = 0; i < sums1.size(); ++i)
334 {
335 const long long need = target_ll - sums1[i].first;
336
337 // binary search in sums2 for 'need'
338 size_t lo = 0, hi = sums2.size();
339 while (lo < hi)
340 {
341 const size_t mid = lo + (hi - lo) / 2;
342 if (sums2[mid].first < need)
343 lo = mid + 1;
344 else
345 hi = mid;
346 }
347
348 if (lo < sums2.size() and sums2[lo].first == need)
349 {
350 // reconstruct indices
352 const uint64_t mask1 = sums1[i].second;
353 const uint64_t mask2 = sums2[lo].second;
354
355 for (size_t j = 0; j < half1; ++j)
356 if (mask1 & (static_cast<uint64_t>(1) << j))
357 sel.append(j);
358
359 for (size_t j = 0; j < half2; ++j)
360 if (mask2 & (static_cast<uint64_t>(1) << j))
361 sel.append(half1 + j);
362
363 return Subset_Sum_Result<T>{true, std::move(sel)};
364 }
365 }
366
367 return Subset_Sum_Result<T>{false, Array<size_t>()};
368 }
369} // namespace Aleph
370
371# endif // SUBSET_SUM_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:579
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:522
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
Array< size_t > extract_values_checked(const Array< T > &values, const char *fn_name)
Definition Subset_Sum.H:95
Array< std::pair< long long, uint64_t > > enumerate_sums(const Array< T > &arr, size_t start, size_t len)
Definition Subset_Sum.H:107
size_t to_size_checked(const T value, const char *fn_name, const char *field_name)
Definition Subset_Sum.H:75
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
bool subset_sum_exists(const Array< T > &values, T target)
Subset sum existence check (space-optimized).
Definition Subset_Sum.H:223
size_t subset_sum_count(const Array< T > &values, T target)
Count the number of subsets summing to target.
Definition Subset_Sum.H:267
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.
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:105
Subset_Sum_Result< T > subset_sum_mitm(const Array< T > &values, T target)
Subset sum via meet-in-the-middle (MITM).
Definition Subset_Sum.H:313
Subset_Sum_Result< T > subset_sum(const Array< T > &values, T target)
Subset sum via classical DP with reconstruction.
Definition Subset_Sum.H:147
void introsort(T *a, const long l, const long r, const Compare &cmp=Compare())
Sort an array using introsort (introspective sort).
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
Result of a subset sum computation.
Definition Subset_Sum.H:66
Array< size_t > selected_indices
Indices of selected elements.
Definition Subset_Sum.H:68
bool exists
Whether a valid subset was found.
Definition Subset_Sum.H:67
static int * k
Dynamic array container with automatic resizing.
Comprehensive sorting algorithms and search utilities for Aleph-w.