ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

“多少”?真的吗?这似乎是一个过于模糊的问题。 :p 寻求一种好的方法可能会更有用。
当您说“功能类似于”时,您是什么意思?您是否正在寻找 Boost 中 std::accumulate 的替代品? (如果是,为什么?)您是否正在寻找类似于 std::accumulate 的函数? (如果有,是什么?)
如果您想要类似于 std::accumulate 的东西,大概您也希望它在某些方面有所不同(否则您可以只使用 std::accumulate);您在寻找与 std::accumulate 的哪些不同之处?

1
18 revs, 7 users 64%

其实方法也不少。

int sum_of_elems = 0;

C++03

经典 for 循环: for(std::vector::iterator it = vector.begin(); it != vector.end(); ++it) sum_of_elems += *it;使用标准算法:#include sum_of_elems = std::accumulate(vector.begin(), vector.end(), 0);重要说明:最后一个参数的类型不仅用于初始值,还用于结果的类型。如果你把一个 int 放在那里,即使向量有浮点数,它也会累积 int。如果要对浮点数求和,请将 0 更改为 0.0 或 0.0f(感谢 nneonneo)。另请参阅下面的 C++11 解决方案。

C++11 及更高版本

湾。即使在未来发生变化时也自动跟踪向量类型:#include sum_of_elems = std::accumulate(vector.begin(), vector.end(), decltype(vector)::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++03 中,您可以将 std::for_each 与仿函数一起使用,它只需要比 C++0x lambda 更多的代码行来定义。
为什么您的 lambda 示例使用 for_eachaccumulate 会更简洁(即使它不需要 lambda)
@jalf:你的观点是正确的,我应该在 for_each 中使用 accumulate 但这个例子不是有用的(用于学习目的),因为它表明我们也可以有嵌套的 lambdas :-)
小心使用 accumulate。最后一个参数的类型不仅用于初始值,还用于结果的类型。如果您将 int 放在那里,即使向量有 float,它也会累积 int。结果可能有细微的错误,编译器会在不告诉你的情况下将结果转换回浮点数。
如果您有 accumulate,为什么还要使用 for_each
b
beahacker

最简单的方法是使用 vector<int> Astd:accumulate

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

p
paxdiablo

Prasoon 已经提供了许多不同的(和好的)方法来做到这一点,这里不需要重复。但是,我想建议一种替代方法来提高速度。

如果您要经常这样做,您可能需要考虑对您的向量进行“子类化”,以便单独维护元素的总和(而不是 实际上 子类化向量,即由于缺少虚拟析构函数而不确定 - 我说的更多的是一个包含总和和其中的向量的类,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

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


有趣,但要小心,因为 std::vector 不用于子类化。
抱歉,我应该更清楚 - 您可以使用与 vector 相同的方法创建自己的类,在其中维护一个 has-a 向量,而不是作为一个适当的子类 (is-a)。
这是有问题的,除非您禁用对数据的访问器,包括但不限于 operator[](int)、非常量迭代器......
@paxdiablo 我相信大卫的意思是如果存储在向量中的数据是通过使用 operator[] 或通过非常量迭代器间接操作的。操纵位置的值现在将不同,这将使总和不正确。如果客户端代码能够保持对“子类”向量中任何元素的可变引用,则无法确保总和是正确的。
这种方法会导致基本向量操作的性能损失。
3
3 revs, 2 users 94%

当您可以向后进行求和时,为什么要向前执行求和?鉴于:

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

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


为什么不通过使用模运算符添加素数来循环遍历向量呢? :-)
@paxdiablo 您只需要与 v.size() 相对质数。
-1:vector::size() 返回一个无符号值,使 (v.size() - 1) 之类的表达式生成警告,或者在最坏的情况下生成雷区。
为什么会有这个答案?向后求和是否有优势,或者你只是在拖钓?
@Lynn:如果向量的末端在高速缓存中很热(来自前一个向前的循环),那么是的,在当前的 Intel x86 CPU 上向后循环可能会更快。此外,将循环计数器计数为零可以在 asm 中为编译器节省一条指令,如果它不展开循环,这可能很重要。但是,在向前循环时,预取有时会稍微好一些,所以通常总是向后循环并不是更好。
r
rafak
#include<boost/range/numeric.hpp>
int sum = boost::accumulate(vector, 0);

感谢你的回答。顺便说一句,std::accumulate 和 boost::accumulate 在时间复杂度上的区别是什么?
std 和 boost 的累积时间复杂度是相同的——线性的。在这种情况下, boost::accumulate 比手动发送 begin 和 end 更容易输入。没有真正的区别。
boost::accumulate 只是 std::accumulate 的包装。
非增强方式并不难:#include <numeric>std::accumulate(v.begin(), v.end(), (int64_t)0);。请注意,初始累加器值的类型用作累加器类型,因此如果要将 8 位元素相加成 64 位结果,那就是这样做的。
J
JeJo

也可以像这样使用 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 也需要时间。

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


2022年,这应该是公认的答案
佚名

仅限 C++0x:

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

这与其他地方提到的 BOOST_FOREACH 类似,并且与用于累积或 for_each 的有状态仿函数相比,在更复杂的情况下具有相同的清晰优势。


如果您将 for (int n : v) sum += n; 更改为 for (auto n : v) sum += n;,它将适用于任何矢量模板。我知道 OP 指的是向量<int>,但这种方式稍微更通用:-)
k
kriss

我是一个 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:嗯,效率低下可能是过度的。您必须同时从向量和增量计数器计算有效数据位置,但这两个操作之一应该足够了,但取消引用迭代器的成本可能会更糟。因此,我将删除该词。
优化编译器可以优化索引变量,如果需要,只需使用指针增量。 (它可以使循环退出条件成为与 start + length 的指针比较)。实际的迭代器也应该完全优化掉。请记住,这不是 perl;它完全编译为 asm,而不是解释。
h
hey dude
 #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);
    }

我认为这是我能想到的最简单的
P
Pavan Chandaka

使用 inclusive_scan (C++17 及以上):

优点是您可以获得向量中前“N”个元素的总和。下面是代码。评论中的解释。

要使用 inclusive_scan ,需要包含“数字”标题。

    //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());

使用减少:

我在答案中没有注意到的另一个选项是使用 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>' 用于执行策略。


D
Dhruv Kakadiya

std::accumulate 可能存在溢出问题,因此最好的方法是对更大的数据类型变量进行基于范围的累积以避免溢出问题。

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

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


O
OGCJN

似乎没有人解决对向量中可能包含 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


N
Nick

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

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