"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
Klasyczna pętla:
for(std::vector<int>::iterator it = vector.begin(); it != vector.end();++it)
sum_of_elems +=*it;
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
b. Automatyczne śledzenie typu wektora, nawet w przypadku przyszłych zmian:
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.
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:
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
Oczywiście jest to pseudo-kod i możesz chcieć mieć trochę więcej funkcjonalności, ale pokazuje podstawową koncepcję.
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 summedint 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);
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.
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.
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.
vector<int> v;// and fill with dataint 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.
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:
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
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;
std::accumulate
w Boost? (Jeśli tak, dlaczego?) Czy szukasz funkcji, które wykonują coś podobnegostd::accumulate
? (Jeśli tak, to co?)std::accumulate
, prawdopodobnie chcesz też, żeby był pod innym względem (inaczej możesz po prostu użyćstd::accumulate
); jakiej różnicystd::accumulate
szukasz?Odpowiedzi:
W rzeczywistości istnieje wiele metod.
C ++ 03
Klasyczna pętla:
Za pomocą standardowego algorytmu:
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ń
0
na0.0
lub0.0f
(dzięki nneonneo). Zobacz także rozwiązanie C ++ 11 poniżej.C ++ 11 i wyższy
b. Automatyczne śledzenie typu wektora, nawet w przypadku przyszłych zmian:
Używanie
std::for_each
:Korzystanie z pętli bazującej na zakresie (dzięki Rogerowi Pate'owi):
źródło
std::for_each
z funktorem, wystarczy zdefiniować więcej linii kodu niż lambda C ++ 0x.for_each
?accumulate
byłoby bardziej zwięzły (nawet jeśli nie potrzebujemy lambda)accumulate
środku,for_each
ale czy ten przykład nie jest użyteczny (do celów uczenia się), ponieważ pokazuje, że możemy również zagnieżdżaćaccumulate
. Typ ostatniego argumentu jest używany nie tylko dla wartości początkowej, ale dla typu wyniku. Jeśli umieściszint
tam, będzie gromadziłint
s, nawet jeśli wektor mafloat
. Wynik może być subtelnie niepoprawny, a kompilator przerzuci wynik do liczby zmiennoprzecinkowej bez uprzedzenia.for_each
jeśli maszaccumulate
?Najprostszym sposobem jest użycie
std:accumuate
tematycevector<int> A
:źródło
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-a
zamiastis-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:
Oczywiście jest to pseudo-kod i możesz chcieć mieć trochę więcej funkcjonalności, ale pokazuje podstawową koncepcję.
źródło
std::vector
nie jest przeznaczony do podklas.has-a
w niej wektor, zamiast być odpowiednią podklasą (is-a
).operator[](int)
iteratory inne niż ...Po co przeprowadzać sumowanie do przodu, skoro można to zrobić wstecz ? Dany:
Możemy użyć indeksowania, odliczając wstecz:
Możemy użyć sprawdzania zakresu „indeksowania”, odliczając wstecz (na wszelki wypadek):
Możemy użyć iteratorów odwrotnych w pętli for:
Możemy używać iteratorów do przodu, iterujących do tyłu, w pętli for (oooh, trudne!):
Możemy używać
accumulate
z iteratorami wstecznymi:Możemy użyć
for_each
wyrażenia lambda przy użyciu iteratorów odwrotnych: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.
źródło
v.size()
.źródło
boost::accumulate
to tylko opakowaniestd::accumulate
.#include <numeric>
istd::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.Można również użyć w
std::valarray<T>
ten sposóbNiektórzy mogą nie uznać tej metody za skuteczną, ponieważ rozmiar
valarray
potrzeb musi być tak duży, jak rozmiar wektora i inicjowanievalarray
również zajmie trochę czasu.W takim przypadku nie używaj go i potraktuj jako kolejny sposób podsumowania sekwencji.
źródło
Tylko C ++ 0x:
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.
źródło
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 :-)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:
iteracja po indeksach (bardzo łatwa do odczytania).
Ten drugi jest destrukcyjny, uzyskując dostęp do wektora jak stos:
źródło
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.źródło
int
z indeksu astd::vector
nie 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ówsize_of(int) < size_of(size_t)
). W takim przypadku kod przepełni indeks. preferowane powinno być std :: vector <T> :: size_type .To jest łatwe. C ++ 11 zapewnia łatwy sposób podsumowania elementów wektora.
źródło