找出 std::vector
中所有元素之和的好方法是什么?
假设我有一个向量 std::vector<int> vector
,其中包含一些元素。现在我想找到所有元素的总和。相同的方法有哪些不同?
std::accumulate
的替代品? (如果是,为什么?)您是否正在寻找类似于 std::accumulate
的函数? (如果有,是什么?)
std::accumulate
的东西,大概您也希望它在某些方面有所不同(否则您可以只使用 std::accumulate
);您在寻找与 std::accumulate
的哪些不同之处?
其实方法也不少。
int sum_of_elems = 0;
C++03
经典 for 循环: for(std::vector
C++11 及更高版本
湾。即使在未来发生变化时也自动跟踪向量类型:#include
最简单的方法是使用 vector<int> A
的 std:accumulate
:
#include <numeric>
cout << accumulate(A.begin(), A.end(), 0);
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
不用于子类化。
has-a
向量,而不是作为一个适当的子类 (is-a
)。
operator[](int)
、非常量迭代器......
当您可以向后进行求和时,为什么要向前执行求和?鉴于:
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; });
因此,正如您所看到的,向后求和向量的方法与向前求和向量的方法一样多,其中一些更令人兴奋,并且为一对一的错误提供了更大的机会。
v.size()
相对质数。
#include<boost/range/numeric.hpp>
int sum = boost::accumulate(vector, 0);
boost::accumulate
只是 std::accumulate
的包装。
#include <numeric>
和 std::accumulate(v.begin(), v.end(), (int64_t)0);
。请注意,初始累加器值的类型用作累加器类型,因此如果要将 8 位元素相加成 64 位结果,那就是这样做的。
也可以像这样使用 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
也需要时间。
在这种情况下,不要使用它,而是将其作为总结序列的另一种方式。
仅限 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>,但这种方式稍微更通用:-)
我是一个 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();
}
start + length
的指针比较)。实际的迭代器也应该完全优化掉。请记住,这不是 perl;它完全编译为 asm,而不是解释。
#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);
}
使用 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>'
用于执行策略。
std::accumulate
可能存在溢出问题,因此最好的方法是对更大的数据类型变量进行基于范围的累积以避免溢出问题。
long long sum = 0;
for (const auto &n : vector)
sum += n;
然后使用 static_cast<>
进一步向下转换为适当的数据类型。
似乎没有人解决对向量中可能包含 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
。
这很容易。 C++11 提供了一种简单的方法来总结向量的元素。
sum = 0;
vector<int> vec = {1,2,3,4,5,....}
for(auto i:vec)
sum+=i;
cout<<" The sum is :: "<<sum<<endl;
std::for_each
与仿函数一起使用,它只需要比 C++0x lambda 更多的代码行来定义。for_each
?accumulate
会更简洁(即使它不需要 lambda)for_each
中使用accumulate
但这个例子不是有用的(用于学习目的),因为它表明我们也可以有嵌套的 lambdas :-)accumulate
。最后一个参数的类型不仅用于初始值,还用于结果的类型。如果您将int
放在那里,即使向量有float
,它也会累积int
。结果可能有细微的错误,编译器会在不告诉你的情况下将结果转换回浮点数。accumulate
,为什么还要使用for_each
?