Wie fasse ich Elemente eines C ++ - Vektors zusammen?

240

Was sind die guten Möglichkeiten, um die Summe aller Elemente in a zu finden std::vector?

Angenommen, ich habe einen Vektor std::vector<int> vectormit einigen Elementen. Jetzt möchte ich die Summe aller Elemente finden. Was sind die verschiedenen Möglichkeiten für das gleiche?

Prasoon Saurav
quelle
1
"Wie viele"? "Ja wirklich?" Das scheint eine zu vage Frage zu sein. : p Könnte nützlicher sein, nach einem guten Weg zu fragen .
Jalf
3
Was meinst du mit "Funktion ähnlich wie"? Suchen Sie einen Ersatz für std::accumulatein Boost? (Wenn ja, warum?) Suchen Sie nach Funktionen, die etwas Ähnliches bewirken std::accumulate? (Wenn ja, was?)
James McNellis
4
Wenn Sie etwas Ähnliches möchten std::accumulate, möchten Sie vermutlich auch, dass es in gewisser Hinsicht anders ist (andernfalls könnten Sie es einfach verwenden std::accumulate). Welche Unterschiede std::accumulatesuchen Sie?
CB Bailey

Antworten:

429

Eigentlich gibt es einige Methoden.

int sum_of_elems = 0;

C ++ 03

  1. Klassiker für Loop:

    for(std::vector<int>::iterator it = vector.begin(); it != vector.end(); ++it)
        sum_of_elems += *it;
  2. Verwenden eines Standardalgorithmus:

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

    Wichtiger Hinweis: Der Typ des letzten Arguments wird nicht nur für den Anfangswert verwendet, sondern auch für den Typ des Ergebnisses . Wenn Sie dort ein int einfügen, werden Ints akkumuliert, selbst wenn der Vektor float hat. Wenn Sie Gleitkommazahlen summieren, wechseln Sie 0zu 0.0oder0.0f (dank nneonneo). Siehe auch die C ++ 11-Lösung unten.

C ++ 11 und höher

  1. b. Automatische Verfolgung des Vektortyps auch bei zukünftigen Änderungen:

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

    std::for_each(vector.begin(), vector.end(), [&] (int n) {
        sum_of_elems += n;
    });
  3. Verwenden einer bereichsbasierten for-Schleife (danke an Roger Pate):

    for (auto& n : vector)
        sum_of_elems += n;
Prasoon Saurav
quelle
6
Natürlich können Sie in C ++ 03 std::for_eacheinen Funktor verwenden. Die Definition erfordert nur mehr Codezeilen als das C ++ 0x-Lambda.
Ben Voigt
8
Warum verwenden Ihre Lambda-Beispiele for_each? accumulatewäre prägnanter (auch wenn es das Lambda nicht braucht)
Jalf
4
@jalf: Ihr Punkt ist richtig, soll ich verwendet habe , accumulateinnen for_eachaber ist nicht dieses Beispiel nützlich (zum Zweck Lernen) , da es zeigt , dass wir auch verschachtelte lambdas haben :-)
Prasoon Saurav
58
Sei vorsichtig mit accumulate. Der Typ des letzten Arguments wird nicht nur für den Anfangswert verwendet, sondern auch für den Typ des Ergebnisses. Wenn Sie dort ein setzen int, wird ints auch dann akkumuliert , wenn der Vektor hat float. Das Ergebnis kann auf subtile Weise falsch sein, und der Compiler wandelt das Ergebnis wieder in einen Float um, ohne es Ihnen mitzuteilen.
Nneonneo
3
Warum würden Sie verwenden, for_eachwenn Sie haben accumulate?
Juanchopanza
46

Der einfachste Weg ist die Verwendung std:accumuatevon vector<int> A:

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

Prasoon hat bereits eine Reihe verschiedener (und guter) Möglichkeiten angeboten, von denen keine hier wiederholt werden muss. Ich möchte jedoch einen alternativen Ansatz für die Geschwindigkeit vorschlagen.

Wenn Sie vorhaben , dieses ziemlich viel zu tun, können Sie „sub-classing“ Ihr Vektor so dass eine Summe von Elementen zu prüfen , ist separat gepflegt (nicht eigentlich Unterklassierungsvektor, der iffy aufgrund des Fehlens von a virtueller Destruktor - Ich spreche eher von einer Klasse, die die Summe und einen Vektor enthält, has-aals von is-avektorähnlichen Methoden.

Für einen leeren Vektor wird die Summe auf Null gesetzt. Fügen Sie bei jeder Einfügung in den Vektor das einzufügende Element zur Summe hinzu. Subtrahieren Sie es bei jedem Löschen. Grundsätzlich alles abgefangen den zugrunde liegenden Vektor ändern kann, um sicherzustellen, dass die Summe konsistent bleibt.

Auf diese Weise haben Sie eine sehr effiziente O (1) -Methode zum "Berechnen" der Summe zu jedem Zeitpunkt (geben Sie einfach die aktuell berechnete Summe zurück). Das Einfügen und Löschen dauert etwas länger, wenn Sie die Gesamtsumme anpassen, und Sie sollten diesen Leistungseinbruch berücksichtigen.

Vektoren, bei denen die Summe häufiger benötigt wird als der Vektor geändert wird, profitieren wahrscheinlich von diesem Schema, da die Kosten für die Berechnung der Summe über alle Zugriffe abgeschrieben werden. Wenn Sie nur die Summe jede Stunde benötigen und sich der Vektor dreitausend Mal pro Sekunde ändert, ist dies natürlich nicht geeignet.

So etwas würde ausreichen:

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

Das ist natürlich Pseudocode und Sie möchten vielleicht etwas mehr Funktionalität, aber es zeigt das Grundkonzept.

paxdiablo
quelle
7
interessant, aber seien Sie vorsichtig, da dies std::vectornicht für Unterklassen gedacht ist.
Evan Teran
7
Entschuldigung, ich hätte klarer sein sollen - Sie könnten Ihre eigene Klasse mit den gleichen Methoden wie Vektor erstellen, der einen has-aVektor darin beibehält , anstatt eine richtige Unterklasse zu sein ( is-a).
Paxdiablo
1
Dies ist problematisch, es sei denn, Sie deaktivieren die Accessoren für die Daten, einschließlich, aber nicht beschränkt auf operator[](int)nicht
konstante
1
@paxdiablo Ich glaube, David bedeutet, wenn die im Vektor gespeicherten Daten durch die Verwendung des Operators [] oder indirekt durch einen nicht konstanten Iterator manipuliert werden. Der Wert an der manipulierten Position ist jetzt unterschiedlich, wodurch die Summe falsch wird. Es gibt keine Möglichkeit, sicherzustellen, dass die Summe korrekt ist, wenn der Clientcode jemals eine veränderbare Referenz auf ein Element innerhalb des "untergeordneten" Vektors enthalten kann.
Bret Kuhns
2
Dieser Ansatz führt zu Leistungseinbußen bei grundlegenden Vektoroperationen.
Basilevs
23

Warum die Summierung vorwärts durchführen, wenn Sie sie rückwärts ausführen können ? Gegeben:

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

Wir können Subskription verwenden und rückwärts zählen:

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

Wir können bereichsüberprüfte "Subskriptionen" verwenden, die rückwärts zählen (nur für den Fall):

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

Wir können Reverse-Iteratoren in einer for-Schleife verwenden:

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

Wir können Vorwärtsiteratoren verwenden, die rückwärts in einer for-Schleife iterieren (oooh, knifflig!):

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

Wir können accumulatemit Reverse-Iteratoren verwenden:

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

Wir können for_eachmit einem Lambda-Ausdruck unter Verwendung von Umkehriteratoren verwenden:

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

Wie Sie sehen, gibt es also genauso viele Möglichkeiten, den Vektor rückwärts zu summieren wie den Vektor vorwärts, und einige davon sind viel aufregender und bieten weitaus größere Möglichkeiten für Fehler, die nacheinander auftreten.

James McNellis
quelle
36
Und warum nicht auch durch den Vektor blättern, indem Sie mit dem Moduloperator eine Primzahl für den Umlauf hinzufügen? :-)
paxdiablo
3
@paxdiablo Du musst wirklich nur relativ primitiv sein v.size().
Clstrfsck
1
-1: vector :: size () gibt einen vorzeichenlosen Wert zurück, sodass Ausdrücke wie (v.size () - 1) Warnungen generieren oder im schlimmsten Fall ein Minenfeld.
Julien Guertault
1
Warum gibt es diese Antwort? Gibt es einen Vorteil, rückwärts zu summieren, oder trollst du nur?
Lynn
4
@Lynn: Wenn das Ende des Vektors im Cache heiß ist (von einer vorherigen Schleife, die vorwärts ging), kann die Rückwärtsschleife auf aktuellen Intel x86-CPUs messbar schneller sein. Wenn Sie einen Schleifenzähler auf Null herunterzählen, kann der Compiler auch eine Anweisung im asm speichern, die von Bedeutung sein kann, wenn die Schleife nicht abgewickelt wird. Das Vorabrufen funktioniert jedoch manchmal etwas besser, wenn Sie vorwärts schleifen. Daher ist es im Allgemeinen nicht besser, immer rückwärts zu schleifen.
Peter Cordes
15
#include<boost/range/numeric.hpp>
int sum = boost::accumulate(vector, 0);
Rafak
quelle
Danke für die Antwort. Übrigens, was ist der Unterschied zwischen std :: accumulate und boost :: accumulate in der zeitlichen Komplexität?
Prasoon Saurav
1
Die zeitliche Komplexität ist für Standard- und Boost-Akkumulationen gleich - linear. In diesem Fall ist boost :: accumulate einfacher einzugeben als das manuelle Senden von Anfang und Ende. Es gibt keinen wirklichen Unterschied.
Metall
7
boost::accumulateist nur eine Hülle herum std::accumulate.
Rafak
2
Der Non-Boost-Weg ist nicht viel schwieriger: #include <numeric>und std::accumulate(v.begin(), v.end(), (int64_t)0);. Beachten Sie, dass der Typ des anfänglichen Akkumulatorwerts als Akkumulatortyp verwendet wird. Wenn Sie also 8-Bit-Elemente zu einem 64-Bit-Ergebnis zusammenfassen möchten, gehen Sie wie folgt vor.
Peter Cordes
6

Man kann auch verwendet werden, std::valarray<T>wie dies

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

Einige finden diesen Weg möglicherweise nicht effizient, da die Größe von valarrayso groß sein muss wie die Größe des Vektors, und die Initialisierung valarraywird ebenfalls Zeit in Anspruch nehmen.

Verwenden Sie es in diesem Fall nicht und nehmen Sie es als eine weitere Möglichkeit, die Sequenz zusammenzufassen.

NeutronStar
quelle
5

Nur C ++ 0x:

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

Dies ähnelt dem an anderer Stelle erwähnten BOOST_FOREACH und hat den gleichen Vorteil der Klarheit in komplexeren Situationen im Vergleich zu Stateful-Funktoren, die mit accumulate oder for_each verwendet werden.


quelle
3
Wenn Sie ändern for (int n : v) sum += n;in for (auto n : v) sum += n;es mit jeder Vektor - Vorlage arbeiten. Ich wusste, dass sich OP auf den Vektor <int> bezieht, aber dieser Weg ist etwas allgemeiner :-)
Jonas
5

Ich bin ein Perl-Benutzer. Ein Spiel, das wir haben, besteht darin, alle Möglichkeiten zu finden, um eine Variable zu erhöhen. Das ist hier nicht wirklich anders. Die Antwort darauf, wie viele Möglichkeiten es gibt, die Summe der Elemente eines Vektors in C ++ zu finden, ist wahrscheinlich an infinity...

Meine 2 Cent:

Verwenden Sie BOOST_FOREACH, um sich von der hässlichen Iteratorsyntax zu befreien:

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

Iteration auf Indizes (sehr einfach zu lesen).

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

Dieser andere ist destruktiv und greift wie ein Stapel auf einen Vektor zu:

while (!myvector.empty()){
   sum+=myvector.back();
   myvector.pop_back();
}
kriss
quelle
Warum ist das Iterieren von Indizes Ihrer Meinung nach ineffizient? Was ist Ihre Grundlage, um das zu sagen?
Bobobobo
@ Bobobobo: Nun, ineffizient ist wahrscheinlich übermäßig. Sie müssen beide die effektive Datenposition aus dem Vektor und dem Inkrementzähler berechnen, aber eine dieser beiden Operationen sollte ausreichen, aber die Kosten für die Dereferenzierung von Iteratoren können noch schlimmer sein. Daher werde ich das Wort entfernen.
Kriss
Ein optimierender Compiler kann die Indexvariable optimieren und bei Bedarf einfach ein Zeigerinkrement verwenden. (Dies kann dazu führen, dass die Loop-Exit-Bedingung ein Zeigervergleich ist start + length). Tatsächliche Iteratoren sollten ebenfalls vollständig optimiert werden. Denken Sie daran, es ist nicht Perl; Es ist vollständig zu asm kompiliert und nicht interpretiert.
Peter Cordes
-1

Ich habe den einfachsten Weg gefunden, die Summe aller Elemente eines Vektors zu finden

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

}

In diesem Programm habe ich einen Vektor der Größe 10 und werde mit 1 initialisiert. Ich habe die Summe durch eine einfache Schleife wie im Array berechnet.

Ravi Kumar Yadav
quelle
1
Die intIndizierung von a std::vectorist im Allgemeinen nicht sicher. v.size()ist möglicherweise größer als der höchste Wert, in dem gespeichert werden kann int(beachten Sie, dass bei einigen Zielplattformen und Compilern size_of(int) < size_of(size_t)). In diesem Fall läuft Ihr Code über den Index. std :: vector <T> :: size_type sollte bevorzugt werden.
Vincent Saulue-Laborde
Es ist ein Beispiel @ VincentSaulue-Laborde Sie können den Datentyp und seine Größe entsprechend Ihren Anforderungen verwenden.
Ravi Kumar Yadav
-5

Es ist leicht. C ++ 11 bietet eine einfache Möglichkeit, Elemente eines Vektors zusammenzufassen.

sum = 0; 
vector<int> vec = {1,2,3,4,5,....}
for(auto i:vec) 
   sum+=i;
cout<<" The sum is :: "<<sum<<endl; 
Haresh Karnan
quelle