能否使用std::lower_bound替代迭代器处理函数调用?技术问询
针对高耗时单调函数的高效二分查找方案
核心问题分析
你需要的是针对单调递增函数f(i),找到最小索引i使得f(i)≥阈值t,且满足:
- 函数调用耗时极高,需尽可能减少调用次数
- 搜索空间极大(超过10^15),无法预先存储所有结果
- 希望复用标准库组件,避免从零实现二分查找
你之前尝试的自定义forward迭代器方案效率极低,核心原因是:std::lower_bound对forward迭代器只能执行线性搜索——因为forward迭代器不支持直接跳转到任意位置,只能通过operator++逐个移动,这就导致算法复杂度从O(log n)直接退化成了O(n),完全无法处理大搜索空间。
解决方案1:自定义随机访问迭代器
要让std::lower_bound发挥二分查找的O(log n)效率,我们需要实现一个随机访问迭代器,它支持直接计算并跳转到中间位置,而不需要逐个递增。下面是改进后的迭代器实现:
#include <iostream> #include <unordered_map> #include <algorithm> #include <iterator> long long foo(long long i) { std::cout << "function evaluation:\t" << i << std::endl; return i; } using function_type = long long(*)(long long); template <function_type F> struct FunIterator { // 随机访问迭代器的类型定义 using difference_type = long long; using value_type = long long; using pointer = const value_type*; using reference = const value_type&; using iterator_category = std::random_access_iterator_tag; static std::unordered_map<long long, long long> cache; long long index; FunIterator(long long idx) : index(idx) {} // 随机访问迭代器必需的操作 FunIterator& operator++() { ++index; return *this; } FunIterator operator++(int) { FunIterator tmp = *this; ++index; return tmp; } FunIterator& operator--() { --index; return *this; } FunIterator operator--(int) { FunIterator tmp = *this; --index; return tmp; } FunIterator operator+(difference_type n) const { return FunIterator(index + n); } FunIterator& operator+=(difference_type n) { index += n; return *this; } FunIterator operator-(difference_type n) const { return FunIterator(index - n); } difference_type operator-(const FunIterator& other) const { return index - other.index; } FunIterator& operator-=(difference_type n) { index -= n; return *this; } reference operator*() const { auto it = cache.find(index); if (it != cache.end()) return it->second; auto res = F(index); cache[index] = res; return cache[index]; } pointer operator->() const { return &(**this); } value_type operator[](difference_type n) const { return *(*this + n); } // 比较操作 bool operator==(const FunIterator& other) const { return index == other.index; } bool operator!=(const FunIterator& other) const { return !(*this == other); } bool operator<(const FunIterator& other) const { return index < other.index; } bool operator>(const FunIterator& other) const { return other < *this; } bool operator<=(const FunIterator& other) const { return !(*this > other); } bool operator>=(const FunIterator& other) const { return !(*this < other); } }; template <function_type F> std::unordered_map<long long, long long> FunIterator<F>::cache; template <function_type F> std::pair<FunIterator<F>, FunIterator<F>> makeFunRange(long long begin, long long end) { return {FunIterator<F>(begin), FunIterator<F>(end)}; } int main() { // 测试大搜索空间 auto range = makeFunRange<foo>(0, 1000000000000LL); auto it = std::lower_bound(range.first, range.second, 400000000000LL); std::cout << "Found index: " << it.index << std::endl; }
关键改进点说明
- 迭代器类型变更:将
iterator_category改为std::random_access_iterator_tag,告诉std::lower_bound可以使用随机访问优化,执行O(log n)的二分查找。 - 实现随机访问操作:添加了
operator+、operator-、operator[]等方法,允许直接跳转到任意索引位置,避免了逐个递增的开销。 - 缓存优化:保留了原有的缓存机制,避免重复调用高耗时函数。
运行效果
运行上述代码后,你会发现函数调用次数只有几十次(对应二分查找的log2(1e12)≈40次),而不是线性遍历的数十亿次,效率得到了质的提升。
解决方案2:直接封装标准库二分逻辑
如果你觉得自定义迭代器还是太冗长,其实可以基于std::lower_bound的核心逻辑封装一个专用函数,完全不需要迭代器,直接接收函数和范围:
#include <iostream> #include <functional> long long foo(long long i) { std::cout << "function evaluation:\t" << i << std::endl; return i; } template <typename Func, typename T, typename Index> Index lower_bound_func(Func f, Index left, Index right, T target) { while (left < right) { Index mid = left + (right - left) / 2; // 避免超大数值溢出 auto val = f(mid); if (val < target) { left = mid + 1; } else { right = mid; } } return left; } // 使用示例 int main() { auto idx = lower_bound_func(foo, 0LL, 1000000000000LL, 400000000000LL); std::cout << "Found index: " << idx << std::endl; }
这个方案更简洁,完全复用了和std::lower_bound一致的二分逻辑,同时避免了迭代器的额外开销,也完全符合你的需求场景。
内容的提问来源于stack exchange,提问作者David S.
相关产品推荐
相关产品推荐

