Як підсумувати елементи вектора C ++?


240

Які хороші способи знаходження суми всіх елементів у a std::vector?

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


1
"Як багато"? Дійсно? Це здається надто розпливчастим питанням. : p Може бути корисніше попросити хороший спосіб це зробити.
jalf

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, він накопичить int, навіть якщо вектор плаває. Якщо ви підсумовуєте числа з плаваючою комою, перейдіть 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 (auto& n : vector)
        sum_of_elems += n;

6
Звичайно, в C ++ 03, який ви можете використовувати std::for_eachз функтором, для визначення потрібно лише більше рядків коду, ніж лямбда C ++ 0x.
Бен Войгт

8
Для чого використовуються ваші приклади лямбда for_each? accumulateбуло б більш лаконічним (навіть якщо йому не потрібна лямбда)
яльф

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

34

Prasoon вже запропонував безліч різних (і хороших) способів зробити це, жоден з яких тут не потрібно повторювати. Однак я хотів би запропонувати альтернативний підхід для швидкості.

Якщо ви збираєтеся робити це зовсім небагато, ви можете розглянути «підкласифікацію» вашого вектора, щоб сума елементів підтримувалась окремо (а не насправді вектор підкласифікації, який є iffy через відсутність віртуальний деструктор - я кажу більше про клас, який містить суму і вектор всередині нього, 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
Вибачте, я мав би бути зрозумілішим - ви можете створити свій власний клас тими ж методами, що і вектор, який підтримував has-aвектор всередині нього, а не був належним підкласом ( is-a).
paxdiablo

1
Це проблематично, якщо ви не operator[](int)
ввімкнете доступ

1
@paxdiablo Я вважаю, що Девід означає, якщо дані, що зберігаються у векторі, маніпулюються за допомогою оператора [] або опосередкування через інератор non const. Значення в маніпульованій позиції тепер буде іншим, що зробить суму неправильною. Немає способу переконатися, що сума є правильною, якщо клієнтський код коли-небудь зможе вмістити змінне посилання на будь-який елемент у векторному "підкласовому" векторі.
Брет Кунз

2
Такий підхід спричиняє покарання за основні векторні операції.
Василевс

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 (oooh, хитро!):

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. Крім того, підрахунок лічильника циклу до нуля може зберегти компілятор інструкцію в зоні, яка може бути важливою, якщо вона не розкручує цикл. Попереднє вилучення іноді працює трохи краще, якщо циклічно переходити вперед, тому, як правило, не краще завжди робити цикл назад.
Пітер Кордес

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

Дякую за відповідь. BTW, в чому різниця між std :: нагромаджують і boost :: накопичують у часовій складності?
Prasoon Saurav

1
Складність у часі однакова і для накопичення std і boost - лінійна. У цьому випадку введення boost :: akumu просто простіше, ніж надсилання на початку та в кінці вручну. Немає реальної різниці.
метал

7
boost::accumulateце просто обгортка навколо std::accumulate.
rafak

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, що згадується в інших місцях, і має таку ж перевагу ясності в більш складних ситуаціях, порівняно з великими функторами, що використовуються з нагромадженням або for_each.


3
Якщо ви перейдете for (int n : v) sum += n;на for (auto n : v) sum += n;нього, він працюватиме з будь-яким векторним шаблоном. Я знав, що ОП стосується вектора <int>, але цей спосіб дещо більш загальний :-)
Йонас

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). Фактичні ітератори також повинні повністю оптимізуватися. Пам'ятайте, це не перл; він повністю складений до 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 слід віддати перевагу.
Вінсент Солу-Лейборд

Це приклад @ 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; 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.