使用std::min_element()时保存函数求值
Saving function evaluations while using std::min_element()
假设给定一个二维点的向量,并期望找到具有最小欧几里得范数的点。
这些点被提供为具有以下typedef std::pair<double, double> point_t
的std::vector<point_t> points
。可以使用计算范数
double norm(point_t p)
{
return pow(p.first, 2) + pow(p.second, 2);
}
我自己写循环,我会做以下事情:
auto leastPoint = points.cend();
auto leastNorm = std::numeric_limits<double>::max();
for (auto iter = points.cbegin(), end = points.cend(); iter != end; ++iter)
{
double const currentNorm = norm(*iter);
if (currentNorm < leastNorm)
{
leastNorm = currentNorm;
leastPoint = iter;
}
}
但是应该使用STL算法,而不是连接自己的循环,所以我很想使用以下方法:
auto const leastPoint = std::min_element(points.cbegin(), points.cend(),
[](point_t const lhs, point_t const rhs){ return norm(lhs) < norm(rhs); });
但有一点需要注意:如果n = points.size()
,那么第一个实现需要norm()
的n
评估,但第二个实现则需要2n-2
评估。(至少如果使用这种可能的实现方式)
所以我的问题是,是否存在任何STL算法,我可以用它来找到这一点,但只有n
对norm()
的评估?
注意:
- 我知道Oh的复杂性是一样的,但后者会导致两倍的评估
- 仅仅为了使用STL算法,创建一个单独的向量并用距离填充它似乎有点过头了——对此有不同的意见
- edit:我实际上需要一个向量元素的迭代器来擦除这一点
您可以使用std::accumulate
(在algorithm
标头中):
累计接收:
range
initial value
binary operator
(可选,如果未通过,则调用operator+
)
initial value
和range
的每个元素将被馈送到operator
中,操作员将返回initial value
的类型的结果,该结果将被馈送至具有该范围的下一个元素的operator
的下一调用中,依此类推
示例代码(使用C++11测试GCC 4.9.0):
#include <algorithm>
#include <iostream>
#include <vector>
#include <cmath>
typedef std::pair<double, double> point_t;
struct norm_t {
point_t p;
double norm;
};
double norm(const point_t& p) {
return std::pow(p.first, 2) + std::pow(p.second, 2);
}
norm_t min_norm(const norm_t& x, const point_t& y) {
double ny = norm(y);
if (ny < x.norm)
return {y, ny};
return x;
}
int main() {
std::vector<point_t> v{{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}};
norm_t first_norm{v[0], norm(v[0])};
auto min_norm_point =
std::accumulate(v.begin(), v.end(), first_norm, min_norm);
std::cout << "(" << min_norm_point.p.first << "," << min_norm_point.p.second
<< "): " << min_norm_point.norm << 'n';
}
您可以在functor
中缓存最小范数,以避免额外的计算(请注意:我使用的是关于std::min_element
实现的信息)。第二个元素是找到的最小元素,第一个元素是迭代元素。
struct minimum_norm {
minimum_norm() : cached_norm(-1) {}
bool operator()(const point_t& first, const point_t& second) {
if (cached_norm == -1)
cached_norm = norm(second);
double norm_first = norm(first);
if (norm_first < cached_norm) {
cached_norm = norm_first;
return true;
}
return false;
}
private:
double cached_norm;
};
int main()
{
std::vector<point_t> v{{3, 4}, {5, 6}, {1, 2}, {7, 8}, {9, 10}};
auto result = std::min_element(std::begin(v), std::end(v), minimum_norm());
std::cout << "min element at: " << std::distance(std::begin(v), result) << std::endl;
}
这就是boost迭代器库中的boost::transform_iterator所要解决的问题。然而,修饰迭代器方法存在局限性,C++标准委员会范围工作组正在考虑将范围添加到标准中,这可能会允许一种更实用的管道方法,例如,在不需要临时存储的情况下转换为min_element。
Eric Niebler在他的博客上发布了一些关于靶场的有趣帖子。
不幸的是,考虑到min_element的典型实现方式,transform_iterator并不能完全解决您的问题——每次比较都会取消对两个迭代器的引用,因此您的函数最终仍会被频繁调用。您可以使用boost迭代器_adaptor来实现类似于"caching_transform_iterator"的东西,这样可以避免对每个解引用进行重新计算,但对于norm()这样的东西来说,这可能会有些过头了。如果你有一个更昂贵的计算,这可能是一个有用的技术。
我认为你认为min_element将执行2N-2比较的假设是错误的
根据min_element的c++引用,您可以看到该算法基本上执行N个比较,这是未排序数组的最小值。
以下是www.cplusplus.com(极不可能)失败的情况的副本。
template <class ForwardIterator>
ForwardIterator min_element ( ForwardIterator first, ForwardIterator last )
{
if (first==last) return last;
ForwardIterator smallest = first;
while (++first!=last)
if (*first<*smallest) // or: if (comp(*first,*smallest)) for version (2)
smallest=first;
return smallest;
}
- 在没有定义返回类型的函数中返回布尔值,并将结果保存在无错误的char编译中-为什么
- 是否可以保存带有参数的函数指针以供以后使用?
- 在每个运行时将函数保存到相同的地址
- 如何使用 pybind11 将 python 函数保存到静态 c++ 容器中?
- 为什么添加析构函数(甚至是空的)会破坏我的结构,该结构使用 ref 转发和折叠来保存 ref 或值的副本?
- (UE4)在标头中保存lambda函数
- 使用函数指针时,ESP 未在函数调用中正确保存
- 如何将 std::string 作为构造函数参数传递,并将其保存的 C 字符串存储在 void 指针中?
- 将子类实例保存在父类型变量中并通过父变量使用 Child 函数?
- 无法绑定可变参数函数并保存到 std::函数
- 如何在程序后台运行函数(特别是自动保存函数)?QT / C++
- Boost:在单独的加载/保存函数中非侵入性地序列化类
- 在数组C++中保存函数指针
- 静态关键字保存函数返回的值
- 使用std::min_element()时保存函数求值
- 在内部结构的定义中使用保存函数的类成员变量,这些内部结构将用作unordered_map对象的模板参数
- 在c++类中保存函数
- 如何在加载/保存函数中读取文件
- 如何在c++中发送或保存函数,接收或恢复函数并执行它
- 正在类中保存函数指针