在c ++

时间:2018-01-04 04:54:45

标签: c++ c++11 vector

假设我有以下相同大小的矢量矢量:

std::vector<float> a({1, 1, 1});
std::vector<float> b({2, 2, 2});
std::vector<float> c({4, 4, 5});

我想获得元素方面的平均向量:

std::vector<float> mean({2.333, 2.333, 2.666});

实现这一目标最优雅的方法是什么? 我可以编写for循环来做到这一点,但是想知道是否有更好的方法。

另请注意,我希望解决方案可以扩展到任意数量的向量(为了给出示例,我使用了三个向量)

3 个答案:

答案 0 :(得分:4)

对于按元素操作,您应该使用std::valarray。引物:

std::valarray<float> a { 1, 1, 1 };
std::valarray<float> b { 2, 2, 2 };
std::valarray<float> c { 4, 4, 5 };
std::valarray<float> mean = (a + b + c) / 3.f;
std::vector<float> v{std::begin(mean), std::end(mean)}

这适用于使用GCC 7.2.1的C ++ 11模式。现在你还没有说明你在向量中的喂食方式,所以你想要的并不清楚。如果你事先知道你将要处理多少向量,这应该有效:

std::valarray<float> foo(std::vector<std::valarray<float>> args) {
    assert(args.size() > 0);

    // sum MUST be initialized with a size
    // and in this case, all sizes must be the same
    // otherwise += is undefined behavior
    std::valarray<float> sum(args[0].size());
    for (auto c : args) {
        sum += c;
    }
    return (sum / (float)args.size());
}

答案 1 :(得分:1)

如果你的内部向量总是具有相同的大小,std::vector似乎不是一个好的选择(它会创建不必要的许多小堆分配并减少数据局部性)。更好地使用std::array,或定义您自己的class Vec

#include <vector>
#include <array>
#include <numeric>
#include <algorithm>

template <typename T, std::size_t N>
struct Vec : std::array<T, N> {

    Vec() = default;
    explicit Vec(std::array<T, N> const& a): std::array<T, N>(a) {}

    static Vec zero() { return Vec(std::array<T, N>{0}); }

    Vec operator + (Vec const& rhs) const {
        Vec result;
        std::transform(std::begin(*this), std::end(*this), std::begin(rhs), std::begin(result), std::plus<T>());
        return result;
    }

    template <typename T2>
    Vec operator / (T2 const& rhs) const {
        Vec result;
        std::transform(std::begin(*this), std::end(*this), std::begin(result), [&](T const& lhs) { return lhs/rhs; });
        return result;
    }
};

Vec<float, 3> elementwise_mean(std::vector<Vec<float, 3>> vecvec) {
    return std::accumulate(std::begin(vecvec), std::end(vecvec), Vec<float, 3>::zero()) / vecvec.size();
}

或者你可以懒惰并使用像eigen3这样的专用库。

答案 2 :(得分:0)

&#34;最优雅的方式&#34; 实现OP的目标是一个意见问题,我很害怕,但我们当然可以取代大部分使用标准库中的算法显式for循环。

向量向量可能不是每个用例的最佳数据结构,而且,逐列遍历此对象可能不是非常缓存友好的。但是,即使它是强制性的,我们仍然可以通过逐行遍历容器,在临时向量中累积每列的总和并最终计算平均值来执行所有需要的计算。

此代码段显示了一种可能的(略微更通用的)实现:

#include <iostream>
#include <vector>
#include <array>
#include <iterator>
#include <stdexcept>
#include <algorithm>
#include <functional>

template<class ReturnType = double, class Container>
auto elementwise_mean(Container const& mat)
{
    using MeansType = std::vector<ReturnType>;
    using DistType = typename MeansType::iterator::difference_type;

    auto it_row = std::begin(mat);
    auto n_rows = std::distance(it_row, std::end(mat));
    if ( n_rows == 0 )
        throw std::runtime_error("The container is empty");

    MeansType means(std::begin(*it_row), std::end(*it_row));
    const DistType row_size = means.size();
    if ( row_size == 0 )
        throw std::runtime_error("The first row is empty");

    std::for_each(
        ++it_row, std::end(mat),
        [&means, row_size](auto const& row) {
            if ( row_size != std::distance(std::begin(row), std::end(row)) )
                throw std::runtime_error("A row has a wrong length");

            std::transform(
                means.begin(), means.end(), std::begin(row),
                means.begin(), std::plus()
            );      
        }
    );

    std::for_each(means.begin(), means.end(), [n_rows](auto & a){ a /= n_rows; });

    return means;
}

template<class Container> void print_out(Container const& c);

int main()
{
    std::vector<std::vector<double>> test {
        {1.0, 1.0, 1.0},
        {2.0, 2.0, 2.0},
        {4.0, 4.0, 5.0}
    }; 
    auto means = elementwise_mean(test);
    print_out(means);       // -->  2.33333 2.33333 2.66667

    std::array<int, 4> test2[2] = {
        {{1, 3, -5, 6}},
        {{2, 5, 6, -8}},
    };
    auto means2 = elementwise_mean<float>(test2);
    print_out(means2);      // -->  1.5 4 0.5 -1

    auto means3 = elementwise_mean<int>(test2);
    print_out(means3);      // -->  1 4 0 -1
}

template<class Container>
void print_out(Container const& c)
{
    for ( const auto x : c )
        std::cout << ' ' << x;
    std::cout << '\n';
}