Come riassumere gli elementi di un vettore C ++?


240

Quali sono i buoni modi per trovare la somma di tutti gli elementi in a std::vector?

Supponiamo che io abbia un vettore std::vector<int> vectorcon alcuni elementi al suo interno. Ora voglio trovare la somma di tutti gli elementi. Quali sono i diversi modi per lo stesso?


1
"Quanti"? Veramente? Sembra una domanda troppo vaga. : p Potrebbe essere più utile chiedere un buon modo per farlo.
jalf

3
Cosa intendi quando dici "funzione simile a?" Stai cercando un sostituto di std::accumulatein Boost? (Se sì, perché?) Stai cercando funzioni che fanno qualcosa di simile std::accumulate? (Se sì, cosa?)
James McNellis,

4
Se vuoi qualcosa di simile a std::accumulate, presumibilmente vuoi anche che sia diverso in qualche modo (altrimenti potresti semplicemente usare std::accumulate); che differenza std::accumulatestai cercando?
CB Bailey,

Risposte:


429

In realtà ci sono alcuni metodi.

int sum_of_elems = 0;

C ++ 03

  1. Classico per loop:

    for(std::vector<int>::iterator it = vector.begin(); it != vector.end(); ++it)
        sum_of_elems += *it;
    
  2. Utilizzando un algoritmo standard:

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

    Nota importante: l'ultimo tipo di argomento viene utilizzato non solo per il valore iniziale, ma anche per il tipo di risultato . Se inserisci un int lì, accumulerà ints anche se il vettore ha float. Se si sommano numeri in virgola mobile, passare 0a 0.0o 0.0f(grazie a nneonneo). Vedi anche la soluzione C ++ 11 di seguito.

C ++ 11 e versioni successive

  1. b. Tenere traccia automaticamente del tipo di vettore anche in caso di modifiche future:

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

    std::for_each(vector.begin(), vector.end(), [&] (int n) {
        sum_of_elems += n;
    });
    
  3. Utilizzando un loop basato su range (grazie a Roger Pate):

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

6
Ovviamente in C ++ 03 puoi usarlo std::for_eachcon un functor, ci vogliono solo più righe di codice per definire rispetto al lambda C ++ 0x.
Ben Voigt,

8
Perché usano i tuoi esempi lambda for_each? accumulatesarebbe più conciso (anche se non ha bisogno della lambda)
jalf

4
@jalf: il tuo punto è corretto, avrei dovuto usarlo accumulatedentro for_eachma questo esempio non è utile (a scopo di apprendimento) in quanto mostra che possiamo anche avere
lambdas

58
Stai attento con accumulate. Il tipo dell'ultimo argomento viene utilizzato non solo per il valore iniziale, ma per il tipo di risultato. Se metti un intlì, accumulerà ints anche se il vettore ha float. Il risultato può essere leggermente sbagliato e il compilatore eseguirà il backup del risultato su un float senza dirtelo.
nneonneo,

3
Perché dovresti usare for_eachse hai accumulate?
juanchopanza,

46

Il modo più semplice è usare std:accumuateun vector<int> A:

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

34

Prasoon ha già offerto una serie di modi diversi (e buoni) per farlo, nessuno dei quali deve essere ripetuto qui. Vorrei tuttavia suggerire un approccio alternativo per la velocità.

Se avete intenzione di fare questo un bel po ', si può prendere in considerazione "sub-classificare" il vostro vettore in modo che una somma di elementi è mantenuta a parte (non in realtà vettore che è incerto sub-classing a causa della mancanza di un distruttore virtuale - Sto parlando più di una classe che contiene la somma e un vettore al suo interno, has-apiuttosto che is-ae fornisce i metodi simili al vettore).

Per un vettore vuoto, la somma è impostata su zero. Ad ogni inserimento nel vettore, aggiungi l'elemento da inserire alla somma. Ad ogni cancellazione, sottrarla. Fondamentalmente, tutto ciò che può cambiare il vettore sottostante viene intercettato per garantire che la somma sia mantenuta coerente.

In questo modo, hai un metodo O (1) molto efficiente per "calcolare" la somma in qualsiasi momento (basta restituire la somma attualmente calcolata). L'inserimento e l'eliminazione impiegheranno un po 'più di tempo a regolare il totale e dovresti prendere in considerazione questo risultato.

I vettori in cui la somma è necessaria più spesso della modifica del vettore sono quelli che possono beneficiare di questo schema, poiché il costo del calcolo della somma è ammortizzato su tutti gli accessi. Ovviamente, se hai solo bisogno della somma ogni ora e il vettore cambia tremila volte al secondo, non sarà adatto.

Qualcosa del genere sarebbe sufficiente:

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

Ovviamente, questo è pseudo-codice e potresti voler avere un po 'più di funzionalità, ma mostra il concetto di base.


7
interessante, ma fai attenzione perché std::vectornon è destinato alla sottoclasse.
Evan Teran,

7
Mi dispiace, avrei dovuto essere più chiaro: potresti creare la tua classe con gli stessi metodi del vettore che ha mantenuto un has-avettore al suo interno, anziché essere una sottoclasse corretta ( is-a).
paxdiablo,

1
Ciò è problematico a meno che non si disabiliti gli accessor nei dati, inclusi, ma non solo operator[](int), iteratori non
costanti

1
@paxdiablo Credo che David significhi se i dati memorizzati nel vettore vengono manipolati mediante l'uso dell'operatore [] o indirettamente tramite un iteratore non const. Il valore nella posizione manipolata sarà ora diverso, il che renderà la somma errata. Non c'è modo di assicurare che la somma sia corretta se il codice client è mai in grado di contenere un riferimento mutabile a qualsiasi elemento all'interno del vettore "sottoclasse".
Bret Kuhns,

2
Questo approccio provoca penalità di prestazione per operazioni vettoriali di base.
Basilevs,

23

Perché eseguire la somma in avanti quando è possibile farlo all'indietro ? Dato:

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

Possiamo usare la sottoscrizione, contando all'indietro:

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

Possiamo usare la "sottoscrizione" controllata dal range, contando all'indietro (per ogni evenienza):

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

Possiamo usare iteratori inversi in un ciclo for:

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

Possiamo usare iteratori in avanti, ripetendo indietro, in un ciclo for (oooh, difficile!):

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

Possiamo usare accumulatecon iteratori inversi:

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

Possiamo usare for_eachcon un'espressione lambda usando iteratori inversi:

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

Quindi, come puoi vedere, ci sono tanti modi per sommare il vettore all'indietro quanti sono per sommare il vettore in avanti, e alcuni di questi sono molto più eccitanti e offrono opportunità molto maggiori per errori off-by-one.


36
E perché non scorrere anche il vettore aggiungendo un numero primo con l'operatore modulo per avvolgente? :-)
paxdiablo,

3
@paxdiablo Devi solo essere relativamente privilegiato v.size().
clstrfsck,

1
-1: vector :: size () restituisce un valore senza segno, facendo in modo che espressioni come (v.size () - 1) generino avvisi o un campo minato nei casi peggiori.
Julien Guertault,

1
Perché esiste questa risposta? C'è un vantaggio nel sommare all'indietro o stai solo trollando?
Lynn

4
@Lynn: se la fine del vettore è calda nella cache (da un loop precedente che è andato avanti), allora sì, il looping indietro può essere misurabile in modo più rapido sulle attuali CPU Intel x86. Inoltre, il conteggio di un contatore di cicli fino a zero può salvare il compilatore un'istruzione nell'asm, che può essere significativa se non si svolge il ciclo. Tuttavia, il precaricamento a volte funziona leggermente meglio quando si fa un ciclo in avanti, quindi in generale non è meglio fare sempre un ciclo indietro.
Peter Cordes,

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

Grazie per la risposta. A proposito, qual è la differenza tra std :: accumulate e boost :: accumulate nella complessità temporale?
Prasoon Saurav,

1
La complessità temporale è la stessa per l'accumulo di std e boost - lineare. In questo caso, boost :: accumulate è solo più facile da digitare rispetto all'invio manuale di inizio e fine. Non c'è vera differenza.
metallo,

7
boost::accumulateè solo un involucro in giro std::accumulate.
rafak,

2
Il modo non boost non è molto più difficile: #include <numeric>e std::accumulate(v.begin(), v.end(), (int64_t)0);. Si noti che il tipo di valore dell'accumulatore iniziale viene utilizzato come tipo di accumulatore, quindi se si desidera sommare gli elementi a 8 bit in un risultato a 64 bit, è così che si fa.
Peter Cordes,

6

Si può anche usare in std::valarray<T>questo modo

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

Alcuni potrebbero non trovare questo modo efficiente poiché la dimensione dei valarraybisogni deve essere grande quanto la dimensione del vettore e l'inizializzazione valarrayrichiederà anche tempo.

In tal caso, non usarlo e considerarlo come un altro modo per riassumere la sequenza.


5

Solo C ++ 0x:

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

Questo è simile al BOOST_FOREACH menzionato altrove e ha lo stesso vantaggio della chiarezza in situazioni più complesse, rispetto ai funzionali stateful usati con accumulate o for_each.


3
Se cambi for (int n : v) sum += n;in for (auto n : v) sum += n;esso funzionerà con qualsiasi modello vettoriale. Sapevo che OP si riferisce al vettore <int>, ma in questo modo è leggermente più generale :-)
Jonas

5

Sono un utente Perl, un gioco che abbiamo è quello di trovare modi diversi per incrementare una variabile ... non è molto diverso qui. La risposta a quanti modi per trovare la somma degli elementi di un vettore in C ++ è probabilmente an infinity...

I miei 2 centesimi:

Usando BOOST_FOREACH, per liberarti della brutta sintassi dell'iteratore:

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

iterando sugli indici (davvero facile da leggere).

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

Quest'altro è distruttivo, accedendo al vettore come uno stack:

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

Perché dici che iterare sugli indici è inefficiente? Qual è la tua base per dirlo?
Bobobobo,

@bobobobo: beh, l'inefficienza è probabilmente eccessiva. È necessario calcolare entrambe la posizione effettiva dei dati dal contatore vettoriale e incrementale, ma una di queste due operazioni dovrebbe essere sufficiente, ma il costo di dereferenziare gli iteratori potrebbe essere addirittura peggiore. Quindi rimuoverò la parola.
Kriss,

Un compilatore di ottimizzazione può ottimizzare la variabile di indice e, se lo desidera, utilizzare semplicemente un incremento del puntatore. (Può fare in modo che la condizione di uscita dell'anello sia un confronto puntatore rispetto start + length). Gli iteratori reali dovrebbero anche ottimizzare completamente. Ricorda, non è perl; è completamente compilato in modo da non essere interpretato.
Peter Cordes,

-1

Ho trovato il modo più semplice per trovare la somma di tutti gli elementi di un vettore

#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 questo programma, ho un vettore di dimensione 10 e sono inizializzato da 1. Ho calcolato la somma con un semplice ciclo come in un array.


1
L'uso intper indicizzare a std::vectornon è sicuro in generale. v.size()potrebbe essere maggiore del valore più alto in cui è possibile archiviare int(notare che con alcune piattaforme e compilatori target size_of(int) < size_of(size_t)). In questo caso, il codice trabocca dall'indice. va preferito std :: vector <T> :: size_type .
Vincent Saulue-Laborde,

È un esempio @ VincentSaulue-Laborde Puoi prendere il tipo di dati e le dimensioni in base alle tue esigenze.
Ravi Kumar Yadav,

-5

È facile. C ++ 11 fornisce un modo semplice per riassumere elementi di un vettore.

sum = 0; 
vector<int> vec = {1,2,3,4,5,....}
for(auto i:vec) 
   sum+=i;
cout<<" The sum is :: "<<sum<<endl; 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.