std::accumulate()仅是复杂std::向量的实数部分

std::accumulate() only the real part of a complex std::vector

本文关键字:std 向量 实数部 复杂 accumulate      更新时间:2023-10-16

我曾经取对称(Hermitian)矩阵的和(矩阵在std::vector中),这是一个巨大的浪费,因为虚部总是加为零(我说的是边长为n>1000的巨大矩阵,所以我认为这会有所不同。

现在我只加上矩阵的上三角部分。但我想进一步优化,避免添加复杂的部分,因为我不需要它

我目前使用的是:

std::real(std::accumulate(myMatrix.begin(), myMatrix.end(), std::complex<Real>(0,0)));

这将myMatrix的所有元素添加到std::complex<Real>(0,0),从而得到我需要的和。

但这会增加我向量的实部和虚部,这是浪费!我如何才能写出最优化的版本,只添加矩阵的真实部分?


更新:

虽然我接受了有效的答案,但我发现它比矩阵的实部和虚部求和慢。对于边长为128的矩阵,它慢5%-10%。这令人惊讶。任何其他更快的建议都将不胜感激。

请询问您是否需要其他信息。

Real real_sum = std::accumulate(
    myMatrix.cbegin(), myMatrix.cend(), Real{},
    [](Real const acc, std::complex<Real> const& c) { return acc + std::real(c); }
);

std::accumulate有两个重载,其中一个重载需要一个运算符:

template< class InputIt, class T, class BinaryOperation >
T accumulate( InputIt first, InputIt last, T init,
              BinaryOperation op );

只需提供您自己的,而不是默认为+:

std::accumulate(myMatrix.begin(), myMatrix.end(), Real{0},
    [](Real const& sum, std::complex<Real> const& next){
        return sum + std::real(next);
    });

或者,你可以做一些有趣的事情,比如使用boost::transform_iterator:

auto make_real = [](std::complex<Real> const& c) {
    return std::real(c);
};
std::accumulate(
    boost::make_transform_iterator(myMatrix.begin(), make_real),
    boost::make_transform_iterator(myMatrix.end(), make_real),
    Real{0});

或带量程v3:

accumulate(myMatrix,
    Real{0},
    std::plus<>{},
    [](std::complex<Real> const& c) { return c.real(); }
);

如果real没有过载,并且您可以在boost示例中提供std::real<Real>,在第二个示例中提供&std::complex<Real>::real,那么以上两种情况都会好得多。

使用std::accumulate是绝对必要的吗?

    Real acc = 0;
    for(auto c : myMatrix)
       acc += real(c);

不要误解我的意思,我赞成在合适的时候使用标准算法,但在可读性方面似乎很难击败这个循环。

这与我的g++-4.8.4安装附带的实现相比:

  template<typename _InputIterator, typename _Tp>
    inline _Tp
    accumulate(_InputIterator __first, _InputIterator __last, _Tp __init)
    {
      // concept requirements
      __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
      __glibcxx_requires_valid_range(__first, __last);
      for (; __first != __last; ++__first)
    __init = __init + *__first;
      return __init;
    }

所以你可以看到他们在做同样的事情。