C ++:在类w.r.t的向量上选择argmax。任意表达

时间:2011-02-22 23:19:01

标签: c++ class vector

我无法描述我的问题,所以我举一个例子:

我有一个类描述,里面有几个变量,例如:

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 ++中的这种疯狂?处理这个问题最好的方法是什么?

谢谢!

7 个答案:

答案 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成员或ab成员的线性组合进行比较。我把比较器分成两部分,因为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;
}