Jak podsumować elementy wektora C ++?


240

Jakie są dobre sposoby na znalezienie sumy wszystkich elementów w std::vector?

Załóżmy, że mam wektor std::vector<int> vectorz kilkoma elementami. Teraz chcę znaleźć sumę wszystkich elementów. Jakie są różne sposoby na to samo?


1
"Ile"? Naprawdę? To wydaje się zbyt niejasne pytanie. : p Może być bardziej użyteczne, aby poprosić o dobry sposób na zrobienie tego.
jalf

3
Co masz na myśli mówiąc „funkcja podobna do”? Szukasz zamiennika std::accumulatew Boost? (Jeśli tak, dlaczego?) Czy szukasz funkcji, które wykonują coś podobnego std::accumulate? (Jeśli tak, to co?)
James McNellis,

4
Jeśli chcesz czegoś podobnego std::accumulate, prawdopodobnie chcesz też, żeby był pod innym względem (inaczej możesz po prostu użyć std::accumulate); jakiej różnicy std::accumulateszukasz?
CB Bailey,

Odpowiedzi:


429

W rzeczywistości istnieje wiele metod.

int sum_of_elems = 0;

C ++ 03

  1. Klasyczna pętla:

    for(std::vector<int>::iterator it = vector.begin(); it != vector.end(); ++it)
        sum_of_elems += *it;
  2. Za pomocą standardowego algorytmu:

    #include <numeric>
    
    sum_of_elems = std::accumulate(vector.begin(), vector.end(), 0);

    Ważna uwaga: Typ ostatniego argumentu jest używany nie tylko dla wartości początkowej, ale również dla typu wyniku . Jeśli umieścisz tam liczbę całkowitą, będzie ona kumulować liczbę całkowitą, nawet jeśli wektor ma zmienną liczbę. Jeśli sumujesz liczby zmiennoprzecinkowe, zmień 0na 0.0lub 0.0f(dzięki nneonneo). Zobacz także rozwiązanie C ++ 11 poniżej.

C ++ 11 i wyższy

  1. b. Automatyczne śledzenie typu wektora, nawet w przypadku przyszłych zmian:

    #include <numeric>
    
    sum_of_elems = std::accumulate(vector.begin(), vector.end(),
                                   decltype(vector)::value_type(0));
  2. Używanie std::for_each:

    std::for_each(vector.begin(), vector.end(), [&] (int n) {
        sum_of_elems += n;
    });
  3. Korzystanie z pętli bazującej na zakresie (dzięki Rogerowi Pate'owi):

    for (auto& n : vector)
        sum_of_elems += n;

6
Oczywiście w C ++ 03 można używać std::for_eachz funktorem, wystarczy zdefiniować więcej linii kodu niż lambda C ++ 0x.
Ben Voigt,

8
Dlaczego wykorzystują twoje przykłady lambda for_each? accumulatebyłoby bardziej zwięzły (nawet jeśli nie potrzebujemy lambda)
jalf

4
@jalf: twój punkt jest poprawny, powinienem był użyć go w accumulateśrodku, for_eachale czy ten przykład nie jest użyteczny (do celów uczenia się), ponieważ pokazuje, że możemy również zagnieżdżać
lambdas

58
Ostrożnie z accumulate. Typ ostatniego argumentu jest używany nie tylko dla wartości początkowej, ale dla typu wyniku. Jeśli umieścisz inttam, będzie gromadził ints, nawet jeśli wektor ma float. Wynik może być subtelnie niepoprawny, a kompilator przerzuci wynik do liczby zmiennoprzecinkowej bez uprzedzenia.
nneonneo

3
Dlaczego miałbyś skorzystać, for_eachjeśli masz accumulate?
juanchopanza

46

Najprostszym sposobem jest użycie std:accumuatetematyce vector<int> A:

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

34

Prasoon zaproponował już wiele różnych (i dobrych) sposobów na zrobienie tego, z których żaden nie musi się tutaj powtarzać. Chciałbym jednak zaproponować alternatywne podejście do prędkości.

Jeśli zamierzasz to robić dość często, możesz rozważyć „podklasowanie” swojego wektora, aby suma elementów była utrzymywana osobno (nie tak naprawdę wektor podklasowania, który jest niepewny z powodu braku wirtualny destruktor - mówię więcej klasy, która zawiera sumę oraz wektor w nim, has-azamiast is-a, i zapewnia wektor-Like metod).

W przypadku pustego wektora suma jest ustawiona na zero. Przy każdym wstawieniu do wektora dodaj wstawiany element do sumy. Przy każdym usunięciu odejmij to. Zasadniczo wszystko , co może zmienić wektor bazowy, jest przechwytywane, aby zapewnić spójność sumy.

W ten sposób masz bardzo wydajną metodę O (1) do „obliczania” sumy w dowolnym momencie (wystarczy zwrócić sumę aktualnie obliczoną). Wstawianie i usuwanie potrwa nieco dłużej, gdy dostosujesz sumę, i powinieneś wziąć pod uwagę to uderzenie wydajności.

Wektory, w których suma jest potrzebna częściej niż wektor jest zmieniany, prawdopodobnie skorzystają z tego schematu, ponieważ koszt obliczenia sumy jest amortyzowany dla wszystkich dostępów. Oczywiście, jeśli potrzebujesz tylko sumy co godzinę, a wektor zmienia się trzy tysiące razy na sekundę, nie będzie to odpowiednie.

Wystarczy coś takiego:

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

Oczywiście jest to pseudo-kod i możesz chcieć mieć trochę więcej funkcjonalności, ale pokazuje podstawową koncepcję.


7
ciekawe, ale bądź ostrożny, ponieważ std::vectornie jest przeznaczony do podklas.
Evan Teran,

7
Przepraszam, powinienem był być jaśniejszy - możesz stworzyć własną klasę przy użyciu tych samych metod, co wektor, który utrzymuje has-aw niej wektor, zamiast być odpowiednią podklasą ( is-a).
paxdiablo,

1
Jest to problematyczne, chyba że wyłączysz dostęp do danych, w tym między innymi operator[](int)iteratory inne niż ...
David Rodríguez - dribeas

1
@paxdiablo Uważam, że David oznacza, że ​​dane przechowywane w wektorze są manipulowane za pomocą operatora [] lub pośrednio przez nie-stały iterator. Wartość w manipulowanej pozycji będzie teraz inna, co spowoduje, że suma będzie niepoprawna. Nie ma sposobu, aby upewnić się, że suma jest poprawna, jeśli kod klienta może kiedykolwiek zawierać zmienne odwołanie do dowolnego elementu w wektorze „podklasowanym”.
Bret Kuhns,

2
Takie podejście powoduje obniżenie wydajności dla podstawowych operacji wektorowych.
Basilevs,

23

Po co przeprowadzać sumowanie do przodu, skoro można to zrobić wstecz ? Dany:

std::vector<int> v;     // vector to be summed
int sum_of_elements(0); // result of the summation

Możemy użyć indeksowania, odliczając wstecz:

for (int i(v.size()); i > 0; --i)
    sum_of_elements += v[i-1];

Możemy użyć sprawdzania zakresu „indeksowania”, odliczając wstecz (na wszelki wypadek):

for (int i(v.size()); i > 0; --i)
    sum_of_elements += v.at(i-1);

Możemy użyć iteratorów odwrotnych w pętli for:

for(std::vector<int>::const_reverse_iterator i(v.rbegin()); i != v.rend(); ++i)
    sum_of_elements += *i;

Możemy używać iteratorów do przodu, iterujących do tyłu, w pętli for (oooh, trudne!):

for(std::vector<int>::const_iterator i(v.end()); i != v.begin(); --i)
    sum_of_elements += *(i - 1);

Możemy używać accumulatez iteratorami wstecznymi:

sum_of_elems = std::accumulate(v.rbegin(), v.rend(), 0);

Możemy użyć for_eachwyrażenia lambda przy użyciu iteratorów odwrotnych:

std::for_each(v.rbegin(), v.rend(), [&](int n) { sum_of_elements += n; });

Tak więc, jak widać, istnieje tak samo wiele sposobów na sumowanie wektora do tyłu, jak i na sumowanie wektora do przodu, a niektóre z nich są znacznie bardziej ekscytujące i oferują znacznie większą szansę na błędy indywidualne.


36
A dlaczego nie przełączyć również wektora, dodając liczbę pierwszą z operatorem modułu dla zawinięcia? :-)
paxdiablo

3
@paxdiablo Naprawdę musisz być stosunkowo dobry v.size().
clstrfsck,

1
-1: vector :: size () zwraca wartość bez znaku, dzięki czemu wyrażenia takie jak (v.size () - 1) generują ostrzeżenia lub pole minowe w najgorszych przypadkach.
Julien Guertault

1
Dlaczego ta odpowiedź istnieje? Czy podsumowanie wstecz jest zaletą, czy po prostu trollujesz?
Lynn

4
@ Lynn: Jeśli koniec wektora jest gorący w pamięci podręcznej (z poprzedniej pętli, która poszła do przodu), to tak, zapętlenie do tyłu może być wymiernie szybsze na obecnych procesorach Intel x86. Również odliczenie licznika pętli do zera może zapisać kompilatorowi instrukcję w asm, co może być znaczące, jeśli nie rozwinie pętli. Pobieranie wstępne czasami działa nieco lepiej podczas zapętlania do przodu, więc ogólnie nie zawsze lepiej jest zapętlać wstecz.
Peter Cordes

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

Dziękuję za odpowiedź. BTW jaka jest różnica między std :: akumuluj i doładuj :: akumuluj w złożoności czasowej?
Prasoon Saurav

1
Złożoność czasu jest taka sama dla std i boost akumuluj - liniowa. W takim przypadku doładowanie :: akumulacja jest po prostu łatwiejsze do wpisania niż ręczne wysyłanie początku i końca. Nie ma prawdziwej różnicy.
metal

7
boost::accumulateto tylko opakowanie std::accumulate.
rafak

2
Sposób bez wzmocnienia nie jest trudniejszy: #include <numeric>i std::accumulate(v.begin(), v.end(), (int64_t)0);. Zauważ, że typ początkowej wartości akumulatora jest używany jako typ akumulatora, więc jeśli chcesz zsumować 8-bitowe elementy w wynik 64-bitowy, tak to robisz.
Peter Cordes

6

Można również użyć w std::valarray<T>ten sposób

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

Niektórzy mogą nie uznać tej metody za skuteczną, ponieważ rozmiar valarraypotrzeb musi być tak duży, jak rozmiar wektora i inicjowanie valarrayrównież zajmie trochę czasu.

W takim przypadku nie używaj go i potraktuj jako kolejny sposób podsumowania sekwencji.


5

Tylko C ++ 0x:

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

Jest to podobne do BOOST_FOREACH wspomnianego gdzie indziej i ma tę samą zaletę przejrzystości w bardziej złożonych sytuacjach, w porównaniu z funkcyjnymi stanowymi funktorami używanymi z akumulacją lub dla każdego.


3
Jeśli zmieni for (int n : v) sum += n;się for (auto n : v) sum += n;to będzie działać z każdą szablon wektora. Znam OP odnosi się do wektora <int>, ale ten sposób jest nieco bardziej ogólny :-)
Jonas,

5

Jestem użytkownikiem Perla, grą, którą mamy, jest znalezienie różnych sposobów na zwiększenie zmiennej ... tutaj tak naprawdę nie jest inaczej. Odpowiedź na to, ile sposobów na znalezienie sumy elementów wektora w C ++ jest prawdopodobnie an infinity...

Moje 2 centy:

Używając BOOST_FOREACH, aby uwolnić się od brzydkiej składni iteratora:

sum = 0;
BOOST_FOREACH(int & x, myvector){
  sum += x;
}

iteracja po indeksach (bardzo łatwa do odczytania).

int i, sum = 0;
for (i=0; i<myvector.size(); i++){
  sum += myvector[i];
}

Ten drugi jest destrukcyjny, uzyskując dostęp do wektora jak stos:

while (!myvector.empty()){
   sum+=myvector.back();
   myvector.pop_back();
}

Dlaczego według ciebie iteracja indeksów jest nieefektywna? Jaka jest Twoja podstawa, by to powiedzieć?
bobobobo,

@bobobobo: cóż, nieefektywny jest prawdopodobnie nadmierny. Musisz obliczyć efektywną pozycję danych z wektora i licznika przyrostów, ale jedna z tych dwóch operacji powinna wystarczyć, ale koszt iteratorów dereferencji może być nawet gorszy. Dlatego usunę słowo.
kriss,

Kompilator optymalizacyjny może zoptymalizować zmienną indeksu i użyć przyrostu wskaźnika, jeśli chce. (Może sprawić, że warunek zakończenia pętli będzie porównaniem wskaźnika start + length). Rzeczywiste iteratory powinny również całkowicie się zoptymalizować. Pamiętaj, że to nie perl; jest w pełni skompilowany do asm, nie jest interpretowany.
Peter Cordes

-1

Znalazłem najłatwiejszy sposób na znalezienie sumy wszystkich elementów wektora

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

}

W tym programie mam wektor o rozmiarze 10 i są inicjowane przez 1. Obliczyłem sumę za pomocą prostej pętli jak w tablicy.


1
Korzystanie intz indeksu a std::vectornie jest ogólnie bezpieczne. v.size()może być większa niż najwyższa wartość, którą można zapisać int(zwróć uwagę, że w przypadku niektórych platform docelowych i kompilatorów size_of(int) < size_of(size_t)). W takim przypadku kod przepełni indeks. preferowane powinno być std :: vector <T> :: size_type .
Vincent Saulue-Laborde

To przykład @ VincentSaulue-Laborde Możesz wziąć typ danych i ich rozmiar zgodnie z własnymi wymaganiami.
Ravi Kumar Yadav

-5

To jest łatwe. C ++ 11 zapewnia łatwy sposób podsumowania elementów wektora.

sum = 0; 
vector<int> vec = {1,2,3,4,5,....}
for(auto i:vec) 
   sum+=i;
cout<<" The sum is :: "<<sum<<endl; 
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.