0

I'm kind of having fun with C++11 and trying to come up with a more elegant way of taking a pairwise comparison of two std::vector<double>. For pairwise maximum, I am using the rather inelegant

typedef std::vector<double> vec;
vec pairwiseMax(vec& a, vec& b)
{
    ASSERT(a.size() == b.size());
    vec c(a); // seed with a
    for (unsigned int i = 0; i < a.size(); i++)
    {
        if (b[i] > a[i]) // bigger than
            c[i] = b[i];
    }
    return c;
}

However, using lambdas and std::for_each would seem better, as in the following which extracts the absolute maximum of a std::vector<double> but I'm not coming up with anything.

inline double vecabs(vec v)
{
    if (v.empty()) return 0.0;
    vec::iterator it = 
        std::max_element(v.begin(), v.end(), 
        // Start lambda function
        [](double const& a, double const& b)
        {
            return (std::abs(a) < std::abs(b));
        });
    return *it;
};

After Igor's post, I now have:

vec c;
c.reserve(a.size());
std::transform(a.begin(), a.end(), b.begin(),
    std::back_inserter(c),
    [](double const& d, double const& e)
    { return max(d, e); });

Two questions:

  1. Why not just copy vec a into vec c like I had before?
  2. Isn't using the back_inserter adding a lot of additional operations? If c==a to start with, then fewer inserts to c would be required.

Like this:

vec c(a); // seed with a
std::transform(a.begin(), a.end(), b.begin(),
    c.begin(),
    [](double const& d, double const& e)
    { return max(d, e); });
return c;
Colin
  • 453
  • 4
  • 16
  • Igor's method is more efficient then your original method. If a and b contain uniformly distributed random values, your original method will make 1.5N copies but Igor's method only makes 1N copies. – atb Apr 07 '15 at 18:48

2 Answers2

3
vec c;
c.reserve(a.size());
std::transform(a.begin(), a.end(), b.begin(),
               std::back_inserter(c),
               std::max<double>);
Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85
  • You may want to add a `c.reserve(a.size())` – druckermanly Apr 07 '15 at 01:50
  • @igor - Hadn't found `std::transform` - thanks! Your suggestion doesn't work though because `std::max` has multiple overloads (like here: http://stackoverflow.com/questions/11626304/is-it-possible-to-use-stdaccumulate-with-stdmin). – Colin Apr 07 '15 at 02:45
  • This can be made to work if we use an anonymous function. See my response. – Erik Garrison Jan 27 '16 at 17:17
1

Igor's solution is great, but I had trouble compiling it. I found I needed to wrap std::max in an anonymous function to get it to compile.

Here are two template functions which allow taking the pairwise maximum for an arbitrary number of vectors:

// pairwise maximum                                               
template<typename T>                                              
vector<T> pmax(const std::vector<T>& a, const std::vector<T>& b) {
    std::vector<T> c;                                             
    assert(a.size() == b.size());                                 
    c.reserve(a.size());                                          
    std::transform(a.begin(), a.end(), b.begin(),                 
                   std::back_inserter(c),                         
                   [](T a, T b) { return std::max<T>(a, b); });   
    return c;                                                     
}                                                                 

// maximum of all vectors
template<typename T>                                                      
vector<T> vpmax(const std::vector<std::vector<T>>& vv) {                  
    std::vector<T> c;                                                     
    if (vv.empty()) return c;                                             
    c = vv.front();                                                       
    typename std::vector<std::vector<T> >::const_iterator v = vv.begin(); 
    ++v; // skip the first element                                        
    for ( ; v != vv.end(); ++v) {                                         
        c = pmax(c, *v);                                                  
    }                                                                     
    return c;                                                             
}
Erik Garrison
  • 1,697
  • 1
  • 14
  • 13