我无法描述我的问题,所以我举一个例子:
我有一个类描述,里面有几个变量,例如:
class A{
float a, b, c, d;
}
现在,我维护一个包含许多这些类的vector<A>
。我经常需要做的是找到这个向量中的对象,它满足其中一个参数最大值w.r.t到其他对象。代码看起来像:
int maxi=-1;
float maxa=-1000;
for(int i=0;i<vec.size();i++){
res= vec[i].a;
if(res > maxa) {
maxa= res;
maxi=i;
}
}
return vec[maxi];
但是,有时候我需要找到最大a
的课程,有时最高b
,有时最高0.8*a + 0.2*b
的课程,有时我想要最大a*VAR + b
,其中VAR
是一个在前面分配的变量,等等。换句话说,我需要为每个类计算一个表达式,并取max
。我发现自己在任何地方都复制粘贴,只更改定义res
的单行。
有没有一些很好的方法来避免C ++中的这种疯狂?处理这个问题最好的方法是什么?
谢谢!
答案 0 :(得分:2)
我知道这个线程很老,但我觉得在C ++中实现一个强大的argmax函数非常有用。
然而,据我所知,上面给出的所有示例都依赖于std :: max_element,它在元素之间进行比较(使用仿函数或通过调用运算符&lt;)。如果每个元素的计算成本很高,这可能会很慢。它适用于排序数字和处理简单类,但如果算子更复杂呢?也许计算国际象棋位置的启发式值或产生巨大树等的其他东西。
正如线程启动器所提到的那样,一个真正的argmax只计算一次arg,然后保存它以与其他argmax进行比较。
编辑:好的,我生气了,有太多的空闲时间,所以我创建了一个&lt; C ++ 11和一个带有r值引用的C ++ 11版本,首先是C ++ 11版本:#include <iostream>
#include <algorithm>
#include <iterator>
#include <vector>
template<typename IteratorT, typename HeuristicFunctorT>
IteratorT argmax(IteratorT && it, const IteratorT & end, const HeuristicFunctorT & functor) {
IteratorT best(it++);
typename HeuristicFunctorT::result_type best_value(functor(*best));
for(; it != end; ++it) {
typename HeuristicFunctorT::result_type value(functor(*it));
if (value > best_value) {
best_value = value;
best = it;
}
}
return best;
}
template<typename IteratorT, typename HeuristicFunctorT>
inline IteratorT argmax(const IteratorT & begin, const IteratorT & end, const HeuristicFunctorT & functor) {
return argmax(IteratorT(begin), end, functor);
}
class IntPairFunctor : public std::unary_function< std::pair<int, int>, int > {
public:
int operator() (const std::pair<int, int> & v) const {
return v.first + v.second;
}
};
std::pair<int, int> rand_pair() {
return std::make_pair(rand(), rand());
}
int main(int argc, const char **argv) {
srand(time(NULL));
std::vector< std::pair<int, int> > ints;
std::generate_n(std::back_insert_iterator< std::vector< std::pair<int, int> > >(ints), 1000, rand_pair);
std::vector< std::pair<int, int> >::iterator m (argmax(ints.begin(), ints.end(), IntPairFunctor()));
std::cout << std::endl << "argmax: " << *m << std::endl;
}
非C ++ 11版本更简单,只有模板:
template<typename IteratorT, typename HeuristicFunctorT>
IteratorT argmax(IteratorT it, const IteratorT & end, const HeuristicFunctorT & functor) {
IteratorT best(it++);
typename HeuristicFunctorT::result_type best_value(functor(*best));
for(; it != end; ++it) {
typename HeuristicFunctorT::result_type value(functor(*it));
if (value > best_value) {
best_value = value;
best = it;
}
}
return best;
}
请注意,两个版本都不需要任何模板参数,唯一的要求是启发式实现unary_function类
答案 1 :(得分:1)
您可以将the std::max_element
algorithm与自定义比较器一起使用。
如果编译器支持lambda表达式,那么编写比较器很容易。
如果没有,您可以编写自定义比较器仿函数。对于仅比较单个成员的简单情况,您可以编写一个通用的“成员比较器”函数对象,它看起来像这样:
template <typename MemberPointer>
struct member_comparator
{
MemberPointer p_;
member_comparator(MemberPointer p) : p_(p) { }
template <typename T>
bool operator()(const T& lhs, const T& rhs) const
{
return lhs.*p_ < rhs.*p_;
}
};
template <typename MemberPointer>
member_comparator<MemberPointer> make_member_comparator(MemberPointer p)
{
return member_comparator<MemberPointer>(p);
}
用作:
// returns an iterator to the element that has the maximum 'd' member:
std::max_element(v.begin(), v.end(), make_member_comparator(&A::d));
答案 2 :(得分:1)
这就是仿函数和STL的用途:
// A class whose objects perform custom comparisons
class my_comparator
{
public:
explicit my_comparator(float c1, float c2) : c1(c1), c2(c2) {}
// std::max_element calls this on pairs of elements
bool operator() (const A &x, const A &y) const
{
return (x.a*c1 + x.b*c2) < (y.a*c1 + y.b*c2);
}
private:
const float c1, c2;
};
// Returns the "max" element in vec
*std::max_element(vec.begin(), vec.end(), my_comparator(0.8,0.2));
答案 3 :(得分:1)
您可以使用每次提供自定义比较谓词的std::max_element
STL算法。
使用C ++ 0x,你甚至可以使用lambda函数来获得最大的简洁性:
auto maxElement=*std::max_element(vector.begin(), vector.end(), [](const A& Left, const A& Right) {
return (0.8*Left.a + 0.2*Left.b)<(0.8*Right.a + 0.2*Right.b);
});
答案 4 :(得分:1)
表达式总是线性的吗?您可以传入四个系数的数组。如果你需要支持任意表达式,你需要一个仿函数,但如果它只是四个字段的仿射组合,那么就不需要那么复杂了。
答案 5 :(得分:1)
template <typename F>
struct CompareBy
{
bool operator()(const typename F::argument_type& x,
const typename F::argument_type& y)
{ return f(x) < f(y); }
CompareBy(const F& f) : f(f) {}
private:
F f;
};
template <typename T, typename U>
struct Member : std::unary_function<U, T>
{
Member(T U::*ptr) : ptr(ptr) {}
const T& operator()(const U& x) { return x.*ptr; }
private:
T U::*ptr;
};
template <typename F>
CompareBy<F> by(const F& f) { return CompareBy<F>(f); }
template <typename T, typename U>
Member<T, U> mem_ptr(T U::*ptr) { return Member<T, U>(ptr); }
您需要包含<functional>
才能生效。现在使用标题<algorithm>
std::max_element(v.begin(), v.end(), by(mem_ptr(&A::a)));
或
double combination(A x) { return 0.2 * x.a + 0.8 * x.b; }
和
std::max_element(v.begin(), v.end(), by(std::fun_ptr(combination)));
甚至
struct combination : std::unary_function<A, double>
{
combination(double x, double y) : x(x), y(y) {}
double operator()(const A& u) { return x * u.a + y * u.b; }
private:
double x, y;
};
与
std::max_element(v.begin(), v.end(), by(combination(0.2, 0.8)));
按a
成员或a
和b
成员的线性组合进行比较。我把比较器分成两部分,因为mem_ptr
这个东西很有用,值得重用。 std::max_element
的返回值是迭代器到最大值。您可以取消引用它以获取max元素,或者您可以使用std::distance(v.begin(), i)
来查找相应的索引(首先包括<iterator>
)。
有关完整代码,请参阅http://codepad.org/XQTx0vql。
答案 6 :(得分:0)
使用带有自定义函子的max_element / min_element的示例
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
struct A{
float a, b, c, d;
};
struct CompareA {
bool operator()(A const & Left, A const & Right) const {
return Left.a < Right.a;
}
};
int main() {
vector<A> vec;
vec.resize(3);
vec[0].a = 1;
vec[1].a = 2;
vec[2].a = 1.5;
vector<A>::iterator it = std::max_element(vec.begin(), vec.end(), CompareA());
cout << "Largest A: " << it->a << endl;
it = std::min_element(vec.begin(), vec.end(), CompareA());
cout << "Smallest A: " << it->a << endl;
}