Comment résumer les éléments d'un vecteur C ++?

240

Quels sont les bons moyens de trouver la somme de tous les éléments dans un std::vector?

Supposons que j'ai un vecteur std::vector<int> vectorcontenant quelques éléments. Maintenant, je veux trouver la somme de tous les éléments. Quelles sont les différentes façons de faire la même chose?

Prasoon Saurav
la source
1
"Combien"? Vraiment? Cela semble une question trop vague. : p Pourrait être plus utile de demander un bon moyen de le faire.
2010 à 7h17
3
Que voulez-vous dire lorsque vous dites "fonction similaire à"? Vous recherchez un remplaçant std::accumulateà Boost? (Si oui, pourquoi?) Recherchez-vous des fonctions qui font quelque chose de similaire std::accumulate? (Si oui, quoi?)
James McNellis
4
Si vous voulez quelque chose de similaire std::accumulate, vous voudrez probablement aussi qu'il soit différent à certains égards (sinon vous pourriez simplement utiliser std::accumulate); Quelle (s) différence (s) std::accumulaterecherchez-vous?
CB Bailey

Réponses:

429

En fait, il existe plusieurs méthodes.

int sum_of_elems = 0;

C ++ 03

  1. Classique pour boucle:

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

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

    Remarque importante: Le type du dernier argument est utilisé non seulement pour la valeur initiale, mais également pour le type du résultat . Si vous y mettez un int, il accumulera des ints même si le vecteur a flotté. Si vous additionnez des nombres à virgule flottante, passez 0à 0.0ou 0.0f(grâce à nneonneo). Voir également la solution C ++ 11 ci-dessous.

C ++ 11 et supérieur

  1. b. Suivi automatique du type de vecteur, même en cas de modifications futures:

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

    std::for_each(vector.begin(), vector.end(), [&] (int n) {
        sum_of_elems += n;
    });
  3. Utilisation d'une boucle for basée sur une plage (grâce à Roger Pate):

    for (auto& n : vector)
        sum_of_elems += n;
Prasoon Saurav
la source
6
Bien sûr, en C ++ 03, vous pouvez utiliser std::for_eachavec un foncteur, il faut juste plus de lignes de code à définir que le lambda C ++ 0x.
Ben Voigt
8
Pourquoi vos exemples lambda utilisent-ils for_each? accumulateserait plus concis (même s'il n'a pas besoin du lambda)
jalf
4
@jalf: votre point est correct, j'aurais dû l'utiliser à l' accumulateintérieur, for_eachmais cet exemple n'est pas utile (à des fins d'apprentissage) car il montre que nous pouvons également avoir des lambdas imbriqués :-)
Prasoon Saurav
58
Soyez prudent avec accumulate. Le type du dernier argument est utilisé non seulement pour la valeur initiale, mais pour le type du résultat. Si vous y mettez un int, il accumulera ints même si le vecteur a float. Le résultat peut être subtilement erroné, et le compilateur restituera le résultat dans un flottant sans vous le dire.
nneonneo
3
Pourquoi utiliseriez for_each-vous si vous en avez accumulate?
juanchopanza
46

La façon la plus simple est d'utiliser std:accumuateun vector<int> A:

#include <numeric>
cout << accumulate(A.begin(), A.end(), 0);
beahacker
la source
Je voulais corriger la faute de frappe de "accumuler" à "accumuler" par "modifier", mais stackoverflow m'a dit que "les modifications doivent comporter au moins 6 caractères".
aafulei Il y a
34

Prasoon a déjà proposé une multitude de façons différentes (et bonnes) de le faire, dont aucune n'a besoin d'être répétée ici. Je voudrais cependant suggérer une approche alternative pour la vitesse.

Si vous allez faire cela un peu, vous voudrez peut-être envisager de "sous-classer" votre vecteur afin qu'une somme d'éléments soit maintenue séparément (pas réellement un vecteur de sous-classification qui est incertain en raison de l'absence d'un destructeur virtuel - je parle plus d'une classe qui contient la somme et un vecteur, has-aplutôt que is-a, et fournit les méthodes de type vecteur).

Pour un vecteur vide, la somme est mise à zéro. À chaque insertion dans le vecteur, ajoutez l'élément inséré à la somme. À chaque suppression, soustrayez-le. Fondamentalement, tout ce qui peut changer le vecteur sous-jacent est intercepté pour garantir la cohérence de la somme.

De cette façon, vous disposez d'une méthode O (1) très efficace pour «calculer» la somme à tout moment (il suffit de renvoyer la somme actuellement calculée). L'insertion et la suppression prendront un peu plus de temps lorsque vous ajustez le total et vous devez prendre en compte ce résultat de performance.

Les vecteurs où la somme est nécessaire plus souvent que le vecteur n'est changé sont ceux susceptibles de bénéficier de ce schéma, puisque le coût de calcul de la somme est amorti sur tous les accès. Évidemment, si vous n'avez besoin que de la somme toutes les heures et que le vecteur change trois mille fois par seconde, il ne conviendra pas.

Quelque chose comme ça suffirait:

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

Évidemment, c'est du pseudo-code et vous voudrez peut-être avoir un peu plus de fonctionnalités, mais cela montre le concept de base.

paxdiablo
la source
7
intéressant, mais attention car il std::vectorn'est pas destiné au sous-classement.
Evan Teran
7
Désolé, j'aurais dû être plus clair - vous pourriez créer votre propre classe avec les mêmes méthodes que vector qui a maintenu un has-avecteur à l'intérieur, plutôt que d'être une sous-classe appropriée ( is-a).
paxdiablo
1
Ceci est problématique à moins que vous ne désactiviez les accesseurs dans les données, y compris, mais sans s'y limiter operator[](int), les itérateurs non const ...
David Rodríguez - dribeas
1
@paxdiablo Je crois que David veut dire si les données stockées dans le vecteur sont manipulées à l'aide de l'opérateur [] ou indirectes via un itérateur non const. La valeur à la position manipulée sera désormais différente, ce qui rendra la somme incorrecte. Il n'y a aucun moyen de s'assurer que la somme est correcte si le code client est capable de contenir une référence mutable à n'importe quel élément du vecteur "sous-classé".
Bret Kuhns
2
Cette approche entraîne une baisse des performances pour les opérations vectorielles de base.
Basilevs
23

Pourquoi effectuer la sommation vers l'avant alors que vous pouvez la faire vers l'arrière ? Donné:

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

Nous pouvons utiliser l'indice, en comptant à rebours:

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

Nous pouvons utiliser la "souscription" vérifiée par intervalle (au cas où):

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

Nous pouvons utiliser des itérateurs inverses dans une boucle for:

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

Nous pouvons utiliser des itérateurs avant, itérant en arrière, dans une boucle for (oooh, délicat!):

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

Nous pouvons utiliser accumulateavec des itérateurs inversés:

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

Nous pouvons utiliser for_eachavec une expression lambda en utilisant des itérateurs inverses:

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

Ainsi, comme vous pouvez le voir, il existe autant de façons de faire la somme du vecteur en arrière que de faire la somme du vecteur en avant, et certaines d'entre elles sont beaucoup plus intéressantes et offrent beaucoup plus de possibilités d'erreurs ponctuelles.

James McNellis
la source
36
Et pourquoi ne pas parcourir également le vecteur en ajoutant un nombre premier avec l'opérateur de module pour le bouclage? :-)
paxdiablo
3
@paxdiablo Vous n'avez vraiment besoin que d'être relativement premier v.size().
clstrfsck
1
-1: vector :: size () retourne une valeur non signée, faisant des expressions comme (v.size () - 1) générer des avertissements, ou un champ de mines dans les pires cas.
Julien Guertault
1
Pourquoi cette réponse existe-t-elle? Y a-t-il un avantage à résumer en arrière, ou êtes-vous simplement à la traîne?
Lynn
4
@Lynn: Si la fin du vecteur est chaude dans le cache (à partir d'une boucle précédente qui a avancé), alors oui, la boucle en arrière peut être sensiblement plus rapide sur les processeurs Intel x86 actuels. En outre, le comptage d'un compteur de boucles jusqu'à zéro peut sauver le compilateur une instruction dans l'asm, ce qui peut être significatif s'il ne déroule pas la boucle. La lecture anticipée fonctionne parfois un peu mieux lors d'une boucle vers l'avant, donc en général il n'est pas préférable de toujours boucler vers l'arrière.
Peter Cordes
15
#include<boost/range/numeric.hpp>
int sum = boost::accumulate(vector, 0);
rafak
la source
Merci d'avoir répondu. BTW quelle est la différence entre std :: accumulate et boost :: accumulate dans la complexité du temps?
Prasoon Saurav
1
La complexité temporelle est la même pour les accumulations std et boost - linéaire. Dans ce cas, boost :: accumulate est simplement plus facile à taper que d'envoyer manuellement le début et la fin. Il n'y a pas vraiment de différence.
métal
7
boost::accumulateest juste un emballage autour std::accumulate.
rafak
2
La méthode sans boost n'est pas beaucoup plus difficile: #include <numeric>et std::accumulate(v.begin(), v.end(), (int64_t)0);. Notez que le type de la valeur initiale de l'accumulateur est utilisé comme type d'accumulateur, donc si vous voulez additionner des éléments 8 bits en un résultat 64 bits, c'est comme ça que vous le faites.
Peter Cordes
6

On peut aussi utiliser std::valarray<T>comme ça

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

Certains peuvent ne pas trouver cette méthode efficace car la taille de valarraydoit être aussi grande que la taille du vecteur et l'initialisation valarrayprendra également du temps.

Dans ce cas, ne l'utilisez pas et considérez-le comme une autre façon de résumer la séquence.

NeutronStar
la source
5

C ++ 0x uniquement:

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

Ceci est similaire au BOOST_FOREACH mentionné ailleurs et a le même avantage de clarté dans des situations plus complexes, par rapport aux foncteurs avec état utilisés avec accumulate ou for_each.


la source
3
Si vous changez for (int n : v) sum += n;en, for (auto n : v) sum += n;cela fonctionnera avec n'importe quel modèle vectoriel. Je savais que OP se réfère au vecteur <int>, mais cette façon est un peu plus générale :-)
Jonas
5

Je suis un utilisateur de Perl, un jeu que nous avons est de trouver toutes les différentes façons d'incrémenter une variable ... ce n'est pas vraiment différent ici. La réponse au nombre de façons de trouver la somme des éléments d'un vecteur en C ++ est probablement an infinity...

Mes 2 cents:

En utilisant BOOST_FOREACH, pour s'affranchir de la syntaxe de l'itérateur laid:

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

itération sur indices (très facile à lire).

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

Cet autre est destructeur, accédant au vecteur comme une pile:

while (!myvector.empty()){
   sum+=myvector.back();
   myvector.pop_back();
}
kriss
la source
Pourquoi dites-vous que l'itération sur les indices est inefficace? Quelle est votre base pour dire cela?
bobobobo
@bobobobo: eh bien, l'inefficacité est probablement excessive. Vous devez à la fois calculer la position effective des données à partir du vecteur et du compteur d'incréments, mais l'une de ces deux opérations devrait être suffisante, mais le coût de déréférencement des itérateurs peut même être pire. Par conséquent, je supprimerai le mot.
kriss
Un compilateur d'optimisation peut optimiser la variable d'index et simplement utiliser un incrément de pointeur s'il le souhaite. (Cela peut faire de la condition de sortie de boucle une comparaison de pointeur start + length). Les itérateurs réels doivent également être entièrement optimisés. N'oubliez pas que ce n'est pas perl; il est entièrement compilé en asm, non interprété.
Peter Cordes
-1

J'ai trouvé le moyen le plus simple de trouver la somme de tous les éléments d'un vecteur

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

}

Dans ce programme, j'ai un vecteur de taille 10 et suis initialisé par 1. J'ai calculé la somme par une simple boucle comme dans le tableau.

Ravi Kumar Yadav
la source
1
Utiliser intpour indexer un std::vectorn'est pas sûr en général. v.size()peut être plus grand que la valeur la plus élevée qui peut être stockée int(notez qu'avec certaines plateformes et compilateurs cibles size_of(int) < size_of(size_t)). Dans ce cas, votre code débordera l'index. std :: vector <T> :: size_type devrait être préféré.
Vincent Saulue-Laborde
C'est un exemple @ VincentSaulue-Laborde Vous pouvez prendre le type de données et sa taille selon vos besoins.
Ravi Kumar Yadav
-5

C'est facile. C ++ 11 fournit un moyen simple de résumer les éléments d'un vecteur.

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