Каковы хорошие способы найти сумму всех элементов в std::vector?
Предположим, у меня есть вектор std::vector<int> vectorс несколькими элементами. Теперь я хочу найти сумму всех элементов. Каковы разные способы для того же?
"Сколько"? В самом деле? Это кажется слишком расплывчатым вопросом. : p Может быть полезнее попросить хороший способ сделать это.
Джалф
3
Что вы имеете в виду, когда говорите «функция похожа на?» Вы ищете замену std::accumulateв Boost? (Если так, почему?) Вы ищете функции, которые делают что-то похожее std::accumulate? (Если так, то что?)
Джеймс МакНеллис
4
Если вы хотите что-то похожее std::accumulate, возможно, вы также хотите, чтобы оно было другим в некотором отношении (в противном случае вы можете просто использовать std::accumulate); Какую разницу std::accumulateвы ищете?
CB Bailey
Ответы:
429
На самом деле существует довольно много методов.
int sum_of_elems =0;
C ++ 03
Классика для петли:
for(std::vector<int>::iterator it = vector.begin(); it != vector.end();++it)
sum_of_elems +=*it;
Важное примечание: тип последнего аргумента используется не только для начального значения, но и для типа результата . Если вы поместите туда int, он будет накапливать целые, даже если вектор имеет float. Если вы суммируете числа с плавающей запятой, измените 0на 0.0или 0.0f(благодаря nneonneo). Смотрите также решение C ++ 11 ниже.
C ++ 11 и выше
б. Автоматическое отслеживание типа вектора даже в случае будущих изменений:
Конечно, в 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:
Я хотел исправить опечатку от «накапливать» до «накапливать» с помощью «редактирования», но stackoverflow сказал, что «правки должны содержать не менее 6 символов».
aafulei
34
Prasoon уже предложил множество различных (и хороших) способов сделать это, ни один из которых не нужно повторять здесь. Однако я хотел бы предложить альтернативный подход к скорости.
Если вы собираетесь делать это совсем немного, вы можете рассмотреть вопрос о «юге причислять» свой вектор так, чтобы сумма элементов сохраняются отдельно (не на самом деле к югу от причислять вектор, сомнительные из - за отсутствие виртуальный деструктор - я говорю больше о классе, который содержит сумму и вектор внутри него, has-aа не is-aпредоставляет вектороподобные методы).
Для пустого вектора сумма устанавливается равной нулю. При каждой вставке в вектор добавляйте вставляемый элемент в сумму. На каждом удалении вычтите это. По сути, все, что может изменить основной вектор, перехватывается, чтобы обеспечить постоянство суммы.
Таким образом, у вас есть очень эффективный метод O (1) для «вычисления» суммы в любой момент времени (просто верните вычисленную сумму). Вставка и удаление займет немного больше времени, так как вы корректируете общее значение, и вы должны принять во внимание это снижение производительности.
Векторы, в которых сумма необходима чаще, чем вектор, изменяются, и это те, которые могут извлечь выгоду из этой схемы, поскольку стоимость вычисления суммы амортизируется по всем доступам. Очевидно, что если вам нужна только сумма каждый час, а вектор меняется три тысячи раз в секунду, она не подойдет.
Что-то вроде этого будет достаточно:
classUberVector:privateVector<int> vec
privateint sum
publicUberVector():
vec =newVector<int>()
sum =0public getSum():return sum
publicadd(int val):
rc = vec.add(val)if rc == OK:
sum = sum + val
return rc
public delindex (int idx):
val =0if idx >=0and idx < vec.size:
val = vec[idx]
rc = vec.delindex (idx)if rc == OK:
sum = sum - val
return rc
Очевидно, это псевдокод, и вам может потребоваться немного больше функциональности, но он показывает основную концепцию.
интересно, но будьте осторожны, поскольку std::vectorне предназначены для подклассов.
Эван Теран
7
Извините, я должен был быть более понятным - вы могли бы создать свой собственный класс с теми же методами, что и вектор, который поддерживал has-aвектор внутри него, вместо того, чтобы быть надлежащим подклассом ( is-a).
paxdiablo
1
Это проблематично, если вы не отключите методы доступа к данным, включая, operator[](int)
помимо
1
@paxdiablo Полагаю, Дэвид имеет в виду, что данные, хранящиеся в векторе, обрабатываются с помощью оператора [] или с помощью неконстантного итератора. Значение в позиции манипуляции теперь будет другим, что сделает сумму неверной. Невозможно гарантировать, что сумма верна, если клиентский код когда-либо может содержать изменяемую ссылку на любой элемент внутри вектора «подкласса».
Брет Кунс
2
Этот подход приводит к снижению производительности для основных векторных операций.
Basilevs
23
Зачем выполнять суммирование вперед, если вы можете сделать это задом наперед ? Дано:
std::vector<int> v;// vector to be summedint 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с обратными итераторами:
Итак, как вы можете видеть, существует столько же способов суммировать вектор в обратном направлении, сколько и для суммирования вектора в прямом направлении, и некоторые из них являются гораздо более захватывающими и предлагают гораздо более широкие возможности для ошибочных ошибок.
И почему бы не прокрутить вектор, добавив простое число с оператором модуля для обтекания? :-)
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);
Спасибо за ответ. Кстати, в чем разница между std :: накопить и повысить :: накапливать во временной сложности?
Прасун Саурав
1
Временная сложность одинакова для накопления стандартных и повышенных значений - линейная. В этом случае boost :: аккумулировать просто проще набрать, чем отправлять в начале и в конце вручную. Там нет никакой разницы.
металл
7
boost::accumulateэто просто обертка вокруг std::accumulate.
Рафак
2
Путь без повышения не намного сложнее: #include <numeric>и std::accumulate(v.begin(), v.end(), (int64_t)0);. Обратите внимание, что тип начального значения аккумулятора используется в качестве типа аккумулятора, поэтому, если вы хотите суммировать 8-битные элементы в 64-битный результат, вот как вы это делаете.
Некоторые могут не найти этот способ эффективным, поскольку размер valarrayдолжен быть таким же большим, как размер вектора, и инициализация valarrayтакже займет время.
В этом случае не используйте его и воспринимайте как еще один способ суммирования последовательности.
vector<int> v;// and fill with dataint 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 ссылается на 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];}
Этот другой является разрушительным, получая доступ к вектору как стек:
Почему вы говорите, что итерации по индексам неэффективны? На чем вы можете это сказать?
бобобо
@bobobobo: ну, неэффективно, вероятно, чрезмерно. Вы должны оба вычислить эффективную позицию данных из вектора и счетчика приращений, но одной из этих двух операций должно быть достаточно, но стоимость разыменования итераторов может быть даже хуже. Поэтому я удалю слово.
Крис
Оптимизирующий компилятор может оптимизировать переменную индекса и просто использовать приращение указателя, если он этого хочет. (Это может сделать условие выхода из цикла сравнением с указателем start + length). Фактические итераторы также должны полностью оптимизироваться. Помните, это не Perl; он полностью скомпилирован в asm, а не интерпретируется.
Питер Кордес
-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;
std::accumulate
в Boost? (Если так, почему?) Вы ищете функции, которые делают что-то похожееstd::accumulate
? (Если так, то что?)std::accumulate
, возможно, вы также хотите, чтобы оно было другим в некотором отношении (в противном случае вы можете просто использоватьstd::accumulate
); Какую разницуstd::accumulate
вы ищете?Ответы:
На самом деле существует довольно много методов.
C ++ 03
Классика для петли:
Используя стандартный алгоритм:
Важное примечание: тип последнего аргумента используется не только для начального значения, но и для типа результата . Если вы поместите туда int, он будет накапливать целые, даже если вектор имеет float. Если вы суммируете числа с плавающей запятой, измените
0
на0.0
или0.0f
(благодаря nneonneo). Смотрите также решение C ++ 11 ниже.C ++ 11 и выше
б. Автоматическое отслеживание типа вектора даже в случае будущих изменений:
Использование
std::for_each
:Использование цикла for на основе диапазона (спасибо Роджеру Пейту):
источник
std::for_each
с функтором, просто требуется больше строк кода для определения, чем лямбда C ++ 0x.for_each
?accumulate
было бы более кратким (даже если лямбда не нужна)accumulate
внутри,for_each
но этот пример не полезен (для учебы), поскольку он показывает, что мы также можем иметь вложенные лямбды :-)accumulate
. Тип последнего аргумента используется не только для начального значения, но и для типа результата. Если вы положитеint
туда, он будет накапливатьint
s, даже если вектор имеетfloat
. Результат может быть слегка ошибочным, и компилятор приведёт результат обратно к плавающей запятой, не сообщая вам.for_each
если у вас естьaccumulate
?Самый простой способ заключается в использовании
std:accumuate
вvector<int> A
:источник
Prasoon уже предложил множество различных (и хороших) способов сделать это, ни один из которых не нужно повторять здесь. Однако я хотел бы предложить альтернативный подход к скорости.
Если вы собираетесь делать это совсем немного, вы можете рассмотреть вопрос о «юге причислять» свой вектор так, чтобы сумма элементов сохраняются отдельно (не на самом деле к югу от причислять вектор, сомнительные из - за отсутствие виртуальный деструктор - я говорю больше о классе, который содержит сумму и вектор внутри него,
has-a
а неis-a
предоставляет вектороподобные методы).Для пустого вектора сумма устанавливается равной нулю. При каждой вставке в вектор добавляйте вставляемый элемент в сумму. На каждом удалении вычтите это. По сути, все, что может изменить основной вектор, перехватывается, чтобы обеспечить постоянство суммы.
Таким образом, у вас есть очень эффективный метод O (1) для «вычисления» суммы в любой момент времени (просто верните вычисленную сумму). Вставка и удаление займет немного больше времени, так как вы корректируете общее значение, и вы должны принять во внимание это снижение производительности.
Векторы, в которых сумма необходима чаще, чем вектор, изменяются, и это те, которые могут извлечь выгоду из этой схемы, поскольку стоимость вычисления суммы амортизируется по всем доступам. Очевидно, что если вам нужна только сумма каждый час, а вектор меняется три тысячи раз в секунду, она не подойдет.
Что-то вроде этого будет достаточно:
Очевидно, это псевдокод, и вам может потребоваться немного больше функциональности, но он показывает основную концепцию.
источник
std::vector
не предназначены для подклассов.has-a
вектор внутри него, вместо того, чтобы быть надлежащим подклассом (is-a
).operator[](int)
Зачем выполнять суммирование вперед, если вы можете сделать это задом наперед ? Дано:
Мы можем использовать подписку, считая в обратном направлении:
Мы можем использовать проверенную по диапазону «подписку», считая в обратном порядке (на всякий случай):
Мы можем использовать обратные итераторы в цикле for:
Мы можем использовать прямые итераторы, повторяющиеся в обратном направлении, в цикле for (ооо, сложно!):
Мы можем использовать
accumulate
с обратными итераторами:Мы можем использовать
for_each
с лямбда-выражением, используя обратные итераторы:Итак, как вы можете видеть, существует столько же способов суммировать вектор в обратном направлении, сколько и для суммирования вектора в прямом направлении, и некоторые из них являются гораздо более захватывающими и предлагают гораздо более широкие возможности для ошибочных ошибок.
источник
v.size()
.источник
boost::accumulate
это просто обертка вокругstd::accumulate
.#include <numeric>
иstd::accumulate(v.begin(), v.end(), (int64_t)0);
. Обратите внимание, что тип начального значения аккумулятора используется в качестве типа аккумулятора, поэтому, если вы хотите суммировать 8-битные элементы в 64-битный результат, вот как вы это делаете.Можно также использовать
std::valarray<T>
как этоНекоторые могут не найти этот способ эффективным, поскольку размер
valarray
должен быть таким же большим, как размер вектора, и инициализацияvalarray
также займет время.В этом случае не используйте его и воспринимайте как еще один способ суммирования последовательности.
источник
Только C ++ 0x:
Это похоже на BOOST_FOREACH, упомянутый в другом месте, и имеет такое же преимущество ясности в более сложных ситуациях по сравнению с функторами с состоянием, используемыми с накоплением или for_each.
источник
for (int n : v) sum += n;
вfor (auto n : v) sum += n;
это будет работать с любым вектором шаблона. Я знаю, что OP ссылается на vector <int>, но этот путь немного более общий :-)Я - пользователь Perl, и наша игра состоит в том, чтобы находить разные способы приращения переменной ... здесь это не сильно отличается. Ответ на то, сколько способов найти сумму элементов вектора в C ++, возможно
an infinity
...Мои 2 цента:
Используя BOOST_FOREACH, чтобы освободиться от уродливого синтаксиса итератора:
перебирая индексы (действительно легко читается).
Этот другой является разрушительным, получая доступ к вектору как стек:
источник
start + length
). Фактические итераторы также должны полностью оптимизироваться. Помните, это не Perl; он полностью скомпилирован в asm, а не интерпретируется.источник
int
для индексацииstd::vector
в целом небезопасно.v.size()
может быть больше, чем максимальное значение, которое может быть сохраненоint
(обратите внимание, что для некоторых целевых платформ и компиляторовsize_of(int) < size_of(size_t)
). В этом случае ваш код переполнит индекс. std :: vector <T> :: size_type должен быть предпочтительным.Это просто. C ++ 11 предоставляет простой способ суммировать элементы вектора.
источник