Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
subset_sum_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 <cstdint>
38# include <random>
39# include <bitArray.H>
40
41# include <gtest/gtest.h>
42
43# include <Subset_Sum.H>
44
45using namespace Aleph;
46
47namespace
48{
49 size_t brute_subset_count(const Array<int> & vals, const int target)
50 {
51 const size_t n = vals.size();
52 const uint64_t total_masks = static_cast<uint64_t>(1) << n;
53 size_t count = 0;
54 for (uint64_t mask = 0; mask < total_masks; ++mask)
55 {
56 int sum = 0;
57 for (size_t i = 0; i < n; ++i)
58 if (mask & (uint64_t(1) << i))
59 sum += vals[i];
60 if (sum == target)
61 ++count;
62 }
63 return count;
64 }
65
66 bool valid_selection(const Array<int> & vals,
67 const Array<size_t> & selected,
68 const int target)
69 {
70 int sum = 0;
71 BitArray seen(vals.size());
72 for (size_t idx : selected)
73 {
74 if (idx >= vals.size() or seen[idx])
75 return false;
76 seen[idx] = true;
77 sum += vals[idx];
78 }
79 return sum == target;
80 }
81} // namespace
82
83
85{
86 const Array<int> vals;
87 auto r = subset_sum(vals, 0);
88 EXPECT_TRUE(r.exists);
89 EXPECT_EQ(r.selected_indices.size(), 0u);
90
91 r = subset_sum(vals, 5);
92 EXPECT_FALSE(r.exists);
93}
94
96{
97 Array<int> vals = {1, 2, 3};
98 auto r = subset_sum(vals, 0);
99 EXPECT_TRUE(r.exists);
100 EXPECT_EQ(r.selected_indices.size(), 0u);
101}
102
104{
105 Array<int> vals = {3, 34, 4, 12, 5, 2};
106 auto r = subset_sum(vals, 9);
107 EXPECT_TRUE(r.exists);
108
109 // Verify selected indices sum to target
110 int total = 0;
111 for (size_t selected_indice : r.selected_indices)
113 EXPECT_EQ(total, 9);
114}
115
117{
118 Array<int> vals = {3, 34, 4, 12, 5, 2};
119 auto r = subset_sum(vals, 100);
120 EXPECT_FALSE(r.exists);
121}
122
124{
125 Array<int> vals = {3, 34, 4, 12, 5, 2};
128}
129
131{
132 Array<int> vals = {1, 2, 3, 4, 5};
133 // Subsets summing to 5: {5}, {2,3}, {1,4} = 3
135}
136
138{
139 Array<int> vals = {1, 2, 3};
140 // Only the empty subset sums to 0
142}
143
145{
146 Array<int> vals = {1, 2, 3};
147 EXPECT_THROW(subset_sum(vals, -1), std::domain_error);
148 EXPECT_THROW(subset_sum_exists(vals, -1), std::domain_error);
149 EXPECT_THROW(subset_sum_count(vals, -1), std::domain_error);
150}
151
153{
154 Array<int> vals = {5, -2, 7};
155 EXPECT_THROW(subset_sum(vals, 10), std::domain_error);
156 EXPECT_THROW(subset_sum_exists(vals, 10), std::domain_error);
157 EXPECT_THROW(subset_sum_count(vals, 10), std::domain_error);
158}
159
161{
162 Array<int> vals = {0, 0, 1};
163 // Subsets summing to 1: {1}, {0,1}, {0,1}, {0,0,1} -> 4
165 auto r = subset_sum(vals, 1);
166 EXPECT_TRUE(r.exists);
167 EXPECT_TRUE(valid_selection(vals, r.selected_indices, 1));
168}
169
170
171// Meet-in-the-middle tests
172
174{
176 auto r = subset_sum_mitm(vals, 0);
177 EXPECT_TRUE(r.exists);
178
179 r = subset_sum_mitm(vals, 5);
180 EXPECT_FALSE(r.exists);
181}
182
184{
185 Array<int> vals = {3, 34, 4, 12, 5, 2};
186 auto r = subset_sum_mitm(vals, 9);
187 EXPECT_TRUE(r.exists);
188
189 int total = 0;
190 for (size_t k = 0; k < r.selected_indices.size(); ++k)
191 total += vals[r.selected_indices[k]];
192 EXPECT_EQ(total, 9);
193}
194
196{
197 Array<int> vals = {3, 34, 4, 12, 5, 2};
198 auto r = subset_sum_mitm(vals, 100);
199 EXPECT_FALSE(r.exists);
200}
201
203{
204 // 20 elements to test MITM properly (too many for brute force 2^20 but ok)
206 for (int i = 1; i <= 20; ++i)
207 vals.append(i * 3);
208
209 // Sum of all = 3*(1+...+20) = 3*210 = 630
210 auto r = subset_sum_mitm(vals, 630);
211 EXPECT_TRUE(r.exists);
212
213 // Partial sum
214 auto r2 = subset_sum_mitm(vals, 30); // 3+27=30, or 9+21=30, etc.
215 EXPECT_TRUE(r2.exists);
216
217 int total = 0;
218 for (size_t k = 0; k < r2.selected_indices.size(); ++k)
219 total += vals[r2.selected_indices[k]];
220 EXPECT_EQ(total, 30);
221}
222
224{
225 Array<int> vals = {42};
226 auto r = subset_sum_mitm(vals, 42);
227 EXPECT_TRUE(r.exists);
228 EXPECT_EQ(r.selected_indices.size(), 1u);
229 EXPECT_EQ(r.selected_indices[0], 0u);
230
231 r = subset_sum_mitm(vals, 43);
232 EXPECT_FALSE(r.exists);
233}
234
236{
237 // Compare DP vs MITM on small inputs
238 Array<int> vals = {2, 3, 7, 8, 10};
239
240 for (int target = 0; target <= 30; ++target)
241 {
242 bool dp_ans = subset_sum_exists(vals, target);
243
244 auto mitm_r = subset_sum_mitm(vals, target);
245 EXPECT_EQ(dp_ans, mitm_r.exists)
246 << "Disagreement at target=" << target;
247 }
248}
249
251{
252 std::mt19937 rng(4242);
253 for (int trial = 0; trial < 100; ++trial)
254 {
255 const size_t n = 1 + rng() % 18; // brute-force friendly
257 vals.reserve(n);
258 for (size_t i = 0; i < n; ++i)
259 vals.append(static_cast<int>(rng() % 11)); // non-negative
260
261 const int target = static_cast<int>(rng() % 45);
262
263 const bool exists = subset_sum_exists(vals, target);
264 const size_t count = subset_sum_count(vals, target);
265 const size_t brute_count = brute_subset_count(vals, target);
266
269
270 const auto r = subset_sum(vals, target);
271 EXPECT_EQ(r.exists, brute_count > 0);
272 if (r.exists)
273 EXPECT_TRUE(valid_selection(vals, r.selected_indices, target));
274 }
275}
276
278{
279 std::mt19937 rng(2024);
280 for (int trial = 0; trial < 80; ++trial)
281 {
282 const size_t n = 1 + rng() % 22; // keep brute-force feasible
284 vals.reserve(n);
285 for (size_t i = 0; i < n; ++i)
286 vals.append(static_cast<int>(rng() % 41) - 20); // allow negatives
287
288 const int target = static_cast<int>(rng() % 61) - 30;
289
290 const auto mitm = subset_sum_mitm(vals, target);
291 const bool brute_exists = brute_subset_count(vals, target) > 0;
292 EXPECT_EQ(mitm.exists, brute_exists);
293 if (mitm.exists)
294 EXPECT_TRUE(valid_selection(vals, mitm.selected_indices, target));
295 }
296}
297
299{
301 for (int i = 0; i < 128; ++i)
302 vals.append(1);
303 EXPECT_THROW(subset_sum_mitm(vals, 10), std::out_of_range);
304}
Subset sum algorithms: classical DP and meet-in-the-middle.
Space-efficient bit array implementation.
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:139
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
Contiguous array of bits.
Definition bitArray.H:189
#define TEST(name)
static mt19937 rng
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
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.
bool exists(Container &container, Operation &operation)
Return true if at least one element satisfies a predicate.
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
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
T sum(const Container &container, const T &init=T{})
Compute sum of all elements.
static int * k
gsl_rng * r