Skip to content

Commit 7c73e2a

Browse files
[gnc-autoclear.cpp] prune search if remaining amounts are too small
this optimisation will greatly reduce the search size. first, sort splits by reverse absolute amount. then precompute the positive and negative sum(amounts) of remaining splits. when exploring any branch, if the target is falls outside the range remaining negative to positive sums, then any combination of subsequent splits will not reach target. bail early.
1 parent e4d61b3 commit 7c73e2a

File tree

1 file changed

+33
-3
lines changed

1 file changed

+33
-3
lines changed

libgnucash/app-utils/gnc-autoclear.cpp

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ struct RuntimeMonitor
6969
struct SplitInfo
7070
{
7171
int64_t m_amount;
72+
int64_t m_rem_pos;
73+
int64_t m_rem_neg;
7274
Split* m_split;
7375
};
7476

@@ -111,8 +113,11 @@ subset_sum (SplitInfoVec::const_iterator iter,
111113
SplitInfoVec& path, Solution& solution,
112114
int64_t target, RuntimeMonitor& monitor)
113115
{
114-
DEBUG ("this=%" PRId64" target=%" PRId64 " path=%s",
115-
iter == end ? 0 : iter->m_amount, target, path_to_str (path));
116+
DEBUG ("this=%" PRId64" target=%" PRId64 " rem_pos=%" PRId64
117+
" rem_neg=%" PRId64 " path=%s",
118+
iter == end ? 0 : iter->m_amount, target,
119+
iter == end ? 0 : iter->m_rem_pos,
120+
iter == end ? 0 : iter->m_rem_neg, path_to_str (path));
116121

117122
if (target == 0)
118123
{
@@ -139,6 +144,13 @@ subset_sum (SplitInfoVec::const_iterator iter,
139144
return;
140145
}
141146

147+
if (target < iter->m_rem_neg || target > iter->m_rem_pos)
148+
{
149+
DEBUG ("PRUNE target=%" PRId64 " rem_pos=%" PRId64" rem_neg=%" PRId64,
150+
target, iter->m_rem_pos, iter->m_rem_neg);
151+
return;
152+
}
153+
142154
auto next = std::next(iter);
143155

144156
// 1st path: use current_num
@@ -177,7 +189,7 @@ gnc_account_get_autoclear_splits (Account *account, gnc_numeric toclear_value,
177189
else if (amt == 0)
178190
DEBUG ("skipping zero-amount split %p", split);
179191
else
180-
splits.push_back ({amt, split});
192+
splits.push_back ({amt, 0, 0, split});
181193
}
182194

183195
static GQuark autoclear_quark = g_quark_from_static_string ("autoclear");
@@ -188,6 +200,24 @@ gnc_account_get_autoclear_splits (Account *account, gnc_numeric toclear_value,
188200
return nullptr;
189201
}
190202

203+
// sort splits in descending absolute amount
204+
std::sort (splits.begin(), splits.end(),
205+
[](const SplitInfo& a, const SplitInfo& b)
206+
{
207+
int64_t aa = std::llabs(a.m_amount);
208+
int64_t bb = std::llabs(b.m_amount);
209+
return (aa == bb) ? a.m_amount > b.m_amount : aa > bb;
210+
});
211+
212+
// for each split, precompute sums of remaining pos or neg amounts
213+
int64_t rem_pos{0}, rem_neg{0};
214+
std::for_each (splits.rbegin(), splits.rend(),
215+
[&rem_pos, &rem_neg](SplitInfo& s)
216+
{
217+
s.m_rem_pos = rem_pos += std::max<int64_t>(s.m_amount, 0);
218+
s.m_rem_neg = rem_neg += std::min<int64_t>(s.m_amount, 0);
219+
});
220+
191221
RuntimeMonitor monitor{max_seconds};
192222
Solution solution;
193223
SplitInfoVec path;

0 commit comments

Comments
 (0)