Как суммировать элементы вектора C ++?

240

Каковы хорошие способы найти сумму всех элементов в std::vector?

Предположим, у меня есть вектор std::vector<int> vectorс несколькими элементами. Теперь я хочу найти сумму всех элементов. Каковы разные способы для того же?

Прасун Саурав
источник
1
"Сколько"? В самом деле? Это кажется слишком расплывчатым вопросом. : p Может быть полезнее попросить хороший способ сделать это.
Джалф
3
Что вы имеете в виду, когда говорите «функция похожа на?» Вы ищете замену std::accumulateв Boost? (Если так, почему?) Вы ищете функции, которые делают что-то похожее std::accumulate? (Если так, то что?)
Джеймс МакНеллис
4
Если вы хотите что-то похожее std::accumulate, возможно, вы также хотите, чтобы оно было другим в некотором отношении (в противном случае вы можете просто использовать std::accumulate); Какую разницу std::accumulateвы ищете?
CB Bailey

Ответы:

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, он будет накапливать целые, даже если вектор имеет float. Если вы суммируете числа с плавающей запятой, измените 0на 0.0или 0.0f(благодаря nneonneo). Смотрите также решение C ++ 11 ниже.

C ++ 11 и выше

  1. б. Автоматическое отслеживание типа вектора даже в случае будущих изменений:

    #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 на основе диапазона (спасибо Роджеру Пейту):

    for (auto& n : vector)
        sum_of_elems += n;
Prasoon Saurav
источник
6
Конечно, в C ++ 03 вы можете использовать std::for_eachс функтором, просто требуется больше строк кода для определения, чем лямбда C ++ 0x.
Бен Фойгт
8
Почему ваши лямбда-примеры используются for_each? accumulateбыло бы более кратким (даже если лямбда не нужна)
jalf
4
@jalf: Ваша точка зрения верна, я должен был использовать accumulateвнутри, for_eachно этот пример не полезен (для учебы), поскольку он показывает, что мы также можем иметь вложенные лямбды :-)
Prasoon Saurav
58
Будьте осторожны с accumulate. Тип последнего аргумента используется не только для начального значения, но и для типа результата. Если вы положите intтуда, он будет накапливать ints, даже если вектор имеет float. Результат может быть слегка ошибочным, и компилятор приведёт результат обратно к плавающей запятой, не сообщая вам.
nneonneo
3
Зачем вам использовать, for_eachесли у вас есть accumulate?
Juanchopanza
46

Самый простой способ заключается в использовании std:accumuateв vector<int> A:

#include <numeric>
cout << accumulate(A.begin(), A.end(), 0);
beahacker
источник
Я хотел исправить опечатку от «накапливать» до «накапливать» с помощью «редактирования», но stackoverflow сказал, что «правки должны содержать не менее 6 символов».
aafulei
34

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

Очевидно, это псевдокод, и вам может потребоваться немного больше функциональности, но он показывает основную концепцию.

paxdiablo
источник
7
интересно, но будьте осторожны, поскольку std::vectorне предназначены для подклассов.
Эван Теран
7
Извините, я должен был быть более понятным - вы могли бы создать свой собственный класс с теми же методами, что и вектор, который поддерживал has-aвектор внутри него, вместо того, чтобы быть надлежащим подклассом ( is-a).
paxdiablo
1
Это проблематично, если вы не отключите методы доступа к данным, включая, operator[](int)
помимо
1
@paxdiablo Полагаю, Дэвид имеет в виду, что данные, хранящиеся в векторе, обрабатываются с помощью оператора [] или с помощью неконстантного итератора. Значение в позиции манипуляции теперь будет другим, что сделает сумму неверной. Невозможно гарантировать, что сумма верна, если клиентский код когда-либо может содержать изменяемую ссылку на любой элемент внутри вектора «подкласса».
Брет Кунс
2
Этот подход приводит к снижению производительности для основных векторных операций.
Basilevs
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с лямбда-выражением, используя обратные итераторы:

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) генерируют предупреждения или минное поле в худших случаях.
Жюльен Геро,
1
Почему этот ответ существует? Есть ли преимущество в суммировании в обратном порядке, или вы просто троллинг?
Линн
4
@Lynn: Если конец вектора горячий в кэше (из предыдущего цикла, который шел вперед), то да, цикл в обратном направлении может быть заметно быстрее на современных процессорах Intel x86. Кроме того, отсчет счетчика цикла до нуля может сохранить компилятору инструкцию в asm, что может быть важно, если он не развернет цикл. Предварительная выборка иногда работает немного лучше при зацикливании вперед, так что в целом не всегда лучше зацикливаться в обратном направлении.
Питер Кордес
15
#include<boost/range/numeric.hpp>
int sum = boost::accumulate(vector, 0);
rafak
источник
Спасибо за ответ. Кстати, в чем разница между std :: накопить и повысить :: накапливать во временной сложности?
Прасун Саурав
1
Временная сложность одинакова для накопления стандартных и повышенных значений - линейная. В этом случае boost :: аккумулировать просто проще набрать, чем отправлять в начале и в конце вручную. Там нет никакой разницы.
металл
7
boost::accumulateэто просто обертка вокруг std::accumulate.
Рафак
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также займет время.

В этом случае не используйте его и воспринимайте как еще один способ суммирования последовательности.

NeutronStar
источник
5

Только C ++ 0x:

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

Это похоже на BOOST_FOREACH, упомянутый в другом месте, и имеет такое же преимущество ясности в более сложных ситуациях по сравнению с функторами с состоянием, используемыми с накоплением или for_each.


источник
3
Если вы меняете for (int n : v) sum += n;в for (auto n : v) sum += n;это будет работать с любым вектором шаблона. Я знаю, что OP ссылается на vector <int>, но этот путь немного более общий :-)
Jonas
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();
}
Kriss
источник
Почему вы говорите, что итерации по индексам неэффективны? На чем вы можете это сказать?
бобобо
@bobobobo: ну, неэффективно, вероятно, чрезмерно. Вы должны оба вычислить эффективную позицию данных из вектора и счетчика приращений, но одной из этих двух операций должно быть достаточно, но стоимость разыменования итераторов может быть даже хуже. Поэтому я удалю слово.
Крис
Оптимизирующий компилятор может оптимизировать переменную индекса и просто использовать приращение указателя, если он этого хочет. (Это может сделать условие выхода из цикла сравнением с указателем 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для индексации std::vectorв целом небезопасно. v.size()может быть больше, чем максимальное значение, которое может быть сохранено int(обратите внимание, что для некоторых целевых платформ и компиляторов size_of(int) < size_of(size_t)). В этом случае ваш код переполнит индекс. std :: vector <T> :: size_type должен быть предпочтительным.
Винсент Солу-Лаборде
Это пример @ 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; 
Хареш Карнан
источник