如何总结C ++向量的元素?


240

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

假设我有一个std::vector<int> vector包含几个元素的向量。现在,我想找到所有元素的总和。相同的不同方式有哪些?


1
“多少”?真?这似乎是一个过于模糊的问题。:p可能更有用的是寻求一种好的方法。
jalf

3
说“功能类似”是什么意思?您是否正在寻找std::accumulateBoost 的替代品?(如果是这样,为什么?)您是否在寻找功能类似于std::accumulate?(如果是,是什么?)
James McNellis

4
如果您想要与相似的东西std::accumulate,想必您也希望它在某些方面有所不同(否则您可以使用std::accumulate);std::accumulate您在寻找什么区别?
CB Bailey 2010年

Answers:


429

其实有很多方法。

int sum_of_elems = 0;

C ++ 03

  1. 经典的循环:

    for(std::vector<int>::iterator it = vector.begin(); it != vector.end(); ++it)
        sum_of_elems += *it;
  2. 使用标准算法:

    #include <numeric>
    
    sum_of_elems = std::accumulate(vector.begin(), vector.end(), 0);

    重要说明:最后一个参数的类型不仅用于初始值,还用于结果的类型。如果在其中放置一个int,即使向量具有浮点数,它也会累积int。如果要对浮点数求和,请更改00.00.0f(感谢nneonneo)。另请参见下面的C ++ 11解决方案。

C ++ 11及更高版本

  1. b。即使在将来发生更改时,也会自动跟踪向量类型:

    #include <numeric>
    
    sum_of_elems = std::accumulate(vector.begin(), vector.end(),
                                   decltype(vector)::value_type(0));
  2. 使用std::for_each

    std::for_each(vector.begin(), vector.end(), [&] (int n) {
        sum_of_elems += n;
    });
  3. 使用基于范围的for循环(感谢Roger Pate):

    for (auto& n : vector)
        sum_of_elems += n;

6
当然,在C ++ 03中,您可以std::for_each与函子一起使用,与C ++ 0x lambda相比,它只需要定义更多的代码行。
Ben Voigt

8
为什么您的lambda示例使用 for_eachaccumulate将会更加简洁(即使它不需要lambda)
jalf

4
@jalf:您的观点是正确的,我应该在accumulate内部使用for_each该示例,但该示例对我们而言不是有用的(出于学习目的),因为它表明我们也可以嵌套lambda :-)
Prasoon Saurav

58
小心accumulate。最后一个参数的类型不仅用于初始值,还用于结果的类型。如果在其中放置intint即使向量具有,它也会累积float。结果可能是错误的,并且编译器会将结果强制转换为浮点型而不通知您。
nneonneo

3
for_each如果有,为什么还要使用accumulate
juanchopanza

46

最简单的方法是使用std:accumuatevector<int> A

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

34

普拉松(Prasoon)已经提供了许多不同的方法来实现此目的,在此无需赘述。我想提出一种替代的速度方法。

如果您要这样做很多,您可能需要考虑对向量进行“子分类”,以便元素的总和被单独维护(实际上不是对向量进行子分类,由于缺少向量,所以很难做到这一点)虚拟析构函数-我说的是更多的类,其中包含sum和一个矢量,has-a而不是is-a,并提供类似于矢量的方法。

对于空向量,总和设置为零。在向量的每次插入中,将要插入的元素添加到总和中。每次删除时,将其减去。基本上什么都可以可以改变基础向量的都会被拦截以确保总和保持一致。

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

需要矢量的总和比更改矢量更频繁的矢量是可能从该方案中受益的矢量,因为计算总和的成本在所有访问中均摊销。显然,如果您每小时仅需要总和,并且向量每秒变化三千次,则不合适。

这样的事情就足够了:

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

显然,这是伪代码,您可能需要更多功能,但是它显示了基本概念。


7
有趣,但要小心,因为std::vector这并不意味着要继承子类。
埃文·特兰

7
抱歉,我应该更清楚一些-您可以使用与vector相同的方法创建自己的类,该方法在其中维护了has-avector,而不是一个适当的子类(is-a)。
paxdiablo

1
这是有问题的,除非你禁用存取到的数据,包括但不限于operator[](int),非const迭代器...
大卫·罗德里格斯- dribeas

1
@paxdiablo我相信David的意思是,存储在向量中的数据是通过使用operator []还是通过非常量迭代器进行间接操纵的。现在,操作位置的值将有所不同,这将使总和不正确。如果客户端代码能够对“子类化”向量中的任何元素进行可变引用,则无法确保总和正确。
Bret Kuhns 2012年

2
这种方法会导致基本向量运算的性能下降。
Basilevs 2012年

23

为什么可以向后进行求和?鉴于:

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; });

因此,正如您所看到的,向后求和向量与向后求和向量的方法之多,其中一些更令人兴奋,并且为一度失误提供了更大的机会。


36
又为什么不通过使用模数运算符添加质数来进行环绕遍历向量呢?:-)
paxdiablo

3
@paxdiablo您实际上只需要相对素v.size()
clstrfsck

1
-1:vector :: size()返回一个无符号值,使类似(v.size()-1)的表达式生成警告,或者在最坏的情况下成为雷区。
Julien Guertault 2014年

1
为什么这个答案存在?向后求和是否有优势,还是您只是在拖钓?
林恩

4
@Lynn:如果向量的末尾在高速缓存中很热(来自向前循环的前一个循环),那么可以,在当前的Intel x86 CPU上,向后循环的速度明显加快。同样,将循环计数器的计数减为零可以为编译器在asm中保存一条指令,如果不展开循环,这将很重要。但是,预取有时在向前循环时效果会稍好一些,因此总的来说,总是向后循环并不是更好。
彼得·科德斯

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

感谢你的回答。顺便说一句,std :: accumulate和boost :: accumate在时间复杂度上有什么区别?
Prasoon Saurav

1
对于std和boost的累加时间复杂度是线性的。在这种情况下,boost :: accumulate比手动发送开始和结束要容易键入。没有真正的区别。
金属

7
boost::accumulate只是一个包装std::accumulate
rafak 2010年

2
非增强方式并不难:#include <numeric>std::accumulate(v.begin(), v.end(), (int64_t)0);。请注意,初始累加器值的类型用作累加器类型,因此,如果要将8位元素求和到64位结果中,就可以这样做。
彼得·科德斯

6

一个人也可以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需求的大小必须与向量的大小一样大,因此某些方法可能找不到这种方法有效,初始化valarray也将花费一些时间。

在这种情况下,请不要使用它,并将其作为总结序列的另一种方法。


5

仅C ++ 0x:

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

这与其他地方提到的BOOST_FOREACH相似,并且在更复杂的情况下具有相同的优点,与与accumate或for_each一起使用的有状态函子相比。


3
如果更改for (int n : v) sum += n;for (auto n : v) sum += n;它,则可与任何矢量模板一起使用。我知道OP指的是vector <int>,但是这种方式更通用:-)
Jonas 2014年

5

我是Perl用户,我们要做的游戏是找到增加变量的各种不同方法……在这里并没有什么不同。在C ++中找到向量元素之和的多少方法的答案可能是an infinity...

我的2美分:

使用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];
}

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

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

为什么说对索引进行迭代效率不高?你这么说的依据是什么?
bobobobo

@bobobobo:好吧,低效率可能是过度的。您既要从向量计数器又要从增量计数器计算有效数据位置,但是这两个操作之一就足够了,但是取消迭代器的代价甚至可能更糟。因此,我将删除该词。
kriss

优化的编译器可以优化掉索引变量,并在需要时仅使用指针增量。(它可以使循环退出条件成为与的指针比较start + length)。实际的迭代器也应该完全优化。记住,这不是perl;它已完全编译为asm,没有解释。
彼得·科德斯

-1

我找到了找到向量所有元素之和的最简单方法

#include <iostream>
#include<vector>
using namespace std;

int main()
{
    vector<int>v(10,1);
    int sum=0;
    for(int i=0;i<v.size();i++)
    {
        sum+=v[i];
    }
    cout<<sum<<endl;

}

在此程序中,我有一个大小为10的向量,并由1初始化。我已经通过数组等简单循环计算了总和。


1
通常,使用int索引a std::vector是不安全的。v.size()可能大于可以存储的最大值int(请注意,对于某些目标平台和编译器而言size_of(int) < size_of(size_t))。在这种情况下,您的代码将使索引溢出。应该首选std :: vector <T> :: size_type
Vincent Saulue-Laborde

这是一个示例@ VincentSaulue-Laborde您可以根据自己的需要选择数据类型和大小。
拉维·库马尔·雅达夫

-5

这很容易。C ++ 11提供了一种汇总向量元素的简便方法。

sum = 0; 
vector<int> vec = {1,2,3,4,5,....}
for(auto i:vec) 
   sum+=i;
cout<<" The sum is :: "<<sum<<endl; 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.