假设您有一个2D点矢量,并且预计会找到Euclidean norm最少的点。
这些点以std::vector<point_t> points
提供以下typedef std::pair<double, double> point_t
。规范可以使用
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()
,那么第一个实现需要n
的{{1}}评估,但第二个实现需要norm()
评估。 (至少在使用this possible implementation时)
所以我的问题是,是否存在任何STL算法,我可以通过该算法找到该点,但仅2n-2
评估n
?
备注:
答案 0 :(得分:3)
您可以使用std::accumulate
(在algorithm
标题中):
累积收到:
range
initial value
binary operator
(可选,如果未通过,则会调用operator+
) initial value
range
以及operator
的每个元素都会被输入initial value
,运算符会返回operator
类型的结果使用范围的下一个元素进行下一次#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';
}
调用,依此类推。
示例代码(使用C ++ 11测试GCC 4.9.0):
functor
您可以在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;
}
中缓存最小规范,以避免额外的计算(请注意:我正在使用有关std::min_element
实施的信息)。第二个元素是最小找到,第一个是迭代元素。
{{1}}
答案 1 :(得分:1)
这是boost::transform_iterator boost iterator library旨在解决的问题。然而,装饰迭代器方法存在局限性,C ++标准委员会Ranges working group正在考虑向标准添加范围,这可能允许更具功能性的管道方法,例如转换为min_element而无需临时存储。
Eric Niebler在blog上有一些关于范围的有趣帖子。
不幸的是,在通常实现min_element的情况下,transform_iterator并没有完全解决你的问题 - 每个比较都会取消引用迭代器,所以你的函数最终会被调用,而不是必要的。您可以使用boost iterator_adaptor来实现类似于&#39; caching_transform_iterator&#39;这避免了重新计算每个dereference,但它可能是像norm()这样的东西。如果您有更昂贵的计算,这可能是一种有用的技术。
答案 2 :(得分:0)
我认为你错误地认为min_element会进行2N-2次比较
Per the c++ reference of min_element你可以看到算法基本上执行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;
}