找出std::vector中所有元素的和的好方法是什么?

假设我有一个向量std::vector<int> vector,其中有几个元素。现在我要求所有元素的和。同样的东西有什么不同的表达方式?


实际上有相当多的方法。

int sum_of_elems = 0;

c++ 03

Classic for loop: for(std::vector<int>::iterator it = vector.begin(); it != vector.end(); ++it) sum_of_elems += *it; Using a standard algorithm: #include <numeric> sum_of_elems = std::accumulate(vector.begin(), vector.end(), 0); Important Note: The last argument's type is used not just for the initial value, but for the type of the result as well. If you put an int there, it will accumulate ints even if the vector has float. If you are summing floating-point numbers, change 0 to 0.0 or 0.0f (thanks to nneonneo). See also the C++11 solution below.

c++ 11及以上版本

b.即使将来发生变化,也会自动跟踪vector类型: # include <数字> Sum_of_elems = std::accumulate(vector.begin(), vector.end(), decltype(向量)::value_type (0)); 利用std:: for_each: std:: for_each (vector.begin (), vector.end (), [&] (int n) { Sum_of_elems += n; }); 使用基于范围的for循环(感谢Roger Pate): For (auto& n: vector) Sum_of_elems += n;

c++ 17及以上版本

使用std::reduce,它也照顾到结果类型,例如,如果你有std::vector<int>,你得到int作为结果。如果你有std::vector<float>,你得到float。或者如果你有std::vector<std::string>,你得到std::string(所有字符串连接)。很有趣,不是吗? 自动结果= std::reduce(v.begin(), v.end()); 这个函数还有其他重载,你甚至可以并行运行,如果你有一个大的集合,你想快速得到结果。


我是一个Perl用户,我们有一个游戏是找到各种不同的方法来增加一个变量…这里没有什么不同。在c++中,有多少种方法可以求出一个向量元素的和,答案可能是无穷大……

我的观点是:

使用BOOST_FOREACH,摆脱丑陋的迭代器语法:

sum = 0;
BOOST_FOREACH(int & x, myvector){
  sum += x;
}

在索引上迭代(非常容易阅读)。

int i, sum = 0;
for (i=0; i<myvector.size(); i++){
  sum += myvector[i];
}

另一个是破坏性的,像访问堆栈一样访问vector:

while (!myvector.empty()){
   sum+=myvector.back();
   myvector.pop_back();
}

prason已经提供了许多不同的(而且很好的)方法来做到这一点,这里没有一个需要重复。不过,我想建议另一种提高速度的方法。

如果你要经常这样做,你可能想要考虑“子类化”你的vector,这样元素的和就被分开维护(实际上不是子类化vector,因为缺少虚析构函数而存在问题——我说的更多的是一个包含和和和的类,has-a而不是is-a,并提供类似vector的方法)。

对于空向量,和设为零。每次插入向量时,将插入的元素加到和中。在每次删除时,减去它。基本上,任何可能改变底层向量的东西都会被拦截,以确保总和保持一致。

这样,您就有了一个非常有效的O(1)方法来“计算”任何时间点的和(只返回当前计算的和)。插入和删除将花费稍长的时间来调整总数,您应该考虑到这种性能影响。

如果向量的和比向量的改变更频繁,那么这些向量可能会从这个方案中受益,因为计算和的成本会在所有访问中摊销。显然,如果你只需要每小时求和,而向量每秒变化3000次,这是不合适的。

这样就足够了:

class UberVector:
    private Vector<int> vec
    private int sum

    public UberVector():
        vec = new Vector<int>()
        sum = 0

    public getSum():
        return sum

    public add (int val):
        rc = vec.add (val)
        if rc == OK:
            sum = sum + val
        return rc

    public delindex (int idx):
        val = 0
        if idx >= 0 and idx < vec.size:
            val = vec[idx]
        rc =  vec.delindex (idx)
        if rc == OK:
            sum = sum - val
        return rc

显然,这是伪代码,您可能希望有更多的功能,但它显示了基本概念。


既然可以倒着做求和,为什么还要往前做呢?考虑到:

std::vector<int> v;     // vector to be summed
int sum_of_elements(0); // result of the summation

我们可以使用下标,向后计数:

for (int i(v.size()); i > 0; --i)
    sum_of_elements += v[i-1];

我们可以使用范围检查的“下标”,向后计数(以防万一):

for (int i(v.size()); i > 0; --i)
    sum_of_elements += v.at(i-1);

我们可以在for循环中使用反向迭代器:

for(std::vector<int>::const_reverse_iterator i(v.rbegin()); i != v.rend(); ++i)
    sum_of_elements += *i;

我们可以在for循环中使用前向迭代器,向后迭代(哦,很棘手!):

for(std::vector<int>::const_iterator i(v.end()); i != v.begin(); --i)
    sum_of_elements += *(i - 1);

我们可以对反向迭代器使用accumulate:

sum_of_elems = std::accumulate(v.rbegin(), v.rend(), 0);

我们可以使用反向迭代器将for_each与lambda表达式一起使用:

std::for_each(v.rbegin(), v.rend(), [&](int n) { sum_of_elements += n; });

所以,正如你所看到的,向后求和的方法和正向求和的方法一样多,其中一些更令人兴奋,并且提供了更大的机会出现差1的错误。


c++ 0 x只:

vector<int> v; // and fill with data
int sum {}; // or = 0 ... :)
for (int n : v) sum += n;

这类似于其他地方提到的BOOST_FOREACH,与与accumulate或for_each一起使用的有状态函子相比,在更复杂的情况下具有同样的清晰性。


#include<boost/range/numeric.hpp>
int sum = boost::accumulate(vector, 0);

也可以像这样使用std::valarray<T>

#include<iostream>
#include<vector>
#include<valarray>

int main()
{
    std::vector<int> seq{ 1,2,3,4,5,6,7,8,9,10 };
    std::valarray<int> seq_add{ seq.data(), seq.size() };
    std::cout << "sum = " << seq_add.sum() << "\n";

    return 0;
}

有些人可能不觉得这种方法有效,因为valarray的大小需要和vector的大小一样大,并且初始化valarray也需要时间。

在这种情况下,不要使用它,把它作为另一种对序列求和的方式。


最简单的方法是使用std:accumulate of a vector<int> a:

#include <numeric>
cout << accumulate(A.begin(), A.end(), 0);

这很简单。c++ 11提供了一种简单的方法来对一个向量的元素求和。

sum = 0; 
vector<int> vec = {1,2,3,4,5,....}
for(auto i:vec) 
   sum+=i;
cout<<" The sum is :: "<<sum<<endl; 

 #include<iostream>
    #include<vector>
    #include<numeric>
    using namespace std;
    int main() {
       vector<int> v = {2,7,6,10};
       cout<<"Sum of all the elements are:"<<endl;
       cout<<accumulate(v.begin(),v.end(),0);
    }

似乎没有人能解决向量中可以有NaN值的元素求和的情况,例如numerical_limits<double>::quite_NaN()

我通常会遍历元素并直接检查。

vector<double> x;

//...

size_t n = x.size();

double sum = 0;

for (size_t i = 0; i < n; i++){

  sum += (x[i] == x[i] ? x[i] : 0);

}

它一点都不花哨,也就是说,没有迭代器或任何其他技巧,但我是这样做的。有时,如果在循环中有其他事情要做,我想让代码更具可读性,我就写

double val = x[i];

sum += (val == val ? val : 0);

//...

在循环中,如果需要重用val。


使用inclinclve_scan (c++ 17及以上):

这样做的好处是可以得到一个向量中前“N”个元素的和。下面是代码。在评论中解释。

要使用inclinclve_scan,需要包含“numeric”标头。

    //INPUT VECTOR
    std::vector<int> data{ 3, 1, 4, 1, 5, 9, 2, 6 };

    //OUTPUT VECTOR WITH SUMS
    //FIRST ELEMENT - 3 
    //SECOND ELEMENT - 3 + 1 
    //THIRD ELEMENT - 3 + 1 + 4 
    //FOURTH ELEMENT - 3 + 1 + 4 + 1
    // ..
    // ..
    //LAST ELEMENT - 3 + 1 + 4 + 1 + 5 + 9 + 2 + 6
    std::vector<int> sums(data.size());

    //SUM ALL NUMBERS IN A GIVEN VECTOR.
    inclusive_scan(data.begin(), data.end(),
        sums.begin());

    //SUM OF FIRST 5 ELEMENTS.
    std::cout << "Sum of first 5 elements :: " << sums[4] << std::endl;

    //SUM OF ALL ELEMENTS
    std::cout << "Sum of all elements :: " << sums[data.size() - 1] << std::endl;

还有一种重载,可以指定执行策略。顺序执行或并行执行。需要包括“执行”头。

    //SUM ALL NUMBERS IN A GIVEN VECTOR.
    inclusive_scan(std::execution::par,data.begin(), data.end(),
        sums.begin());

使用reduce:

我在答案中没有注意到的另一个选项是使用c++17中引入的std::reduce。

但是你可能会注意到很多编译器不支持它(GCC 10以上可能就不错了)。但最终支持会来的。

使用std::reduce,优势在于使用执行策略。指定执行策略可选。当指定执行策略为std::execution::par时,算法可能会使用硬件并行处理能力。当使用大尺寸向量时,增益可能会更明显。

例子:

//SAMPLE
std::vector<int> vec = {2,4,6,8,10,12,14,16,18};
    
//WITHOUT EXECUTION POLICY
int sum = std::reduce(vec.begin(),vec.end());
    
//TAKING THE ADVANTAGE OF EXECUTION POLICIES
int sum2 = std::reduce(std::execution::par,vec.begin(),vec.end());
    
std::cout << "Without execution policy  " << sum << std::endl;
std::cout << "With execution policy  " << sum2 << std::endl;

std::reduce需要<numeric>报头。 和'<execution>'用于执行策略。


Std::accumulate可能有溢出问题,所以最好的方法是对较大的数据类型变量进行基于范围的积累,以避免溢出问题。

long long sum = 0;
for (const auto &n : vector)
  sum += n;

然后使用static_cast<>进一步向下转换为适当的数据类型。