285 votes

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

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

Supposons que j'ai un vecteur std::vector vector avec quelques éléments dedans. Maintenant je veux trouver la somme de tous les éléments. Quelles sont les différentes façons d'y parvenir ?

2 votes

"Combien"? Vraiment? Cela semble être une question trop vague. :p Il serait peut-être plus utile de demander une bonne façon de le faire.

3 votes

Que voulez-vous dire lorsque vous dites "fonction similaire à?" Êtes-vous à la recherche d'un remplacement pour std::accumulate dans Boost? (Si oui, pourquoi?) Cherchez-vous des fonctions qui font quelque chose de similaire à std::accumulate? (Si oui, quoi?)

4 votes

Si vous voulez quelque chose de similaire à std::accumulate, vous voulez probablement qu'il soit différent à certains égards (sinon vous pourriez simplement utiliser std::accumulate); quelles différences par rapport à std::accumulate recherchez-vous?

482voto

Prasoon Saurav Points 47488

En fait, il y a plusieurs méthodes.

int sum_of_elems = 0;

C++03

  1. Boucle for classique :

     for(std::vector::iterator it = vector.begin(); it != vector.end(); ++it)
         sum_of_elems += *it;
  2. Utilisation d'un algorithme standard :

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

    Note importante : Le type du dernier argument est utilisé non seulement pour la valeur initiale, mais aussi pour le type du résultat. Si vous mettez un int ici, cela accumulera des ints même si le vecteur contient des flottants. Si vous additionnez des nombres à virgule flottante, modifiez 0 en 0.0 ou 0.0f (merci à nneonneo). Voir également la solution en C++11 ci-dessous.

C++11 et versions supérieures

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

     #include 
    
     sum_of_elems = std::accumulate(vector.begin(), vector.end(),
                                    decltype(vector)::value_type(0));
  2. Utilisation de 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 (merci à Roger Pate) :

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

C++17 et supérieur

  1. Utilisation de std::reduce qui gère également le type de résultat, par exemple si vous avez un std::vector, vous obtenez un int comme résultat. Si vous avez un std::vector, vous obtenez un float. Ou si vous avez un std::vector, vous obtenez une std::string (toutes les chaînes concaténées). Intéressant, n'est-ce pas ?

    #include        
    
    auto result = std::reduce(v.begin(), v.end());

    Il y a d'autres surcharges de cette fonction que vous pouvez exécuter même en parallèle, au cas où vous auriez une grande collection et que vous vouliez obtenir rapidement le résultat.

7 votes

Bien sûr, en C++03, vous pouvez utiliser std::for_each avec un foncteur, il suffit juste de définir plus de lignes de code que la lambda C++0x.

8 votes

Pourquoi vos exemples de lambda utilisent-ils for_each? accumulate serait plus concis (même s'il n'a pas besoin de la lambda)

4 votes

@jalf: votre point est correct, j'aurais dû utiliser accumulate à l'intérieur de for_each, mais n'est pas cet exemple utile (à des fins d'apprentissage) car il montre que nous pouvons également avoir des lambdas imbriquées :-)

35voto

paxdiablo Points 341644

Prasoon a déjà offert toute une série de différentes façons (et bonnes) de faire cela, dont aucune n'a besoin d'être répétée ici. J'aimerais suggérer cependant une approche alternative pour la rapidité.

Si vous envisagez de faire cela assez souvent, vous voudrez peut-être envisager de "sous-classer" votre vecteur de sorte qu'une somme des éléments soit maintenue séparément (pas réellement sous-classer le vecteur ce qui est délicat en raison du manque d'un destructeur virtuel - je parle plutôt d'une classe qui contient la somme et un vecteur en son sein, has-a plutôt que is-a, et fournit les méthodes semblables à un vecteur).

Pour un vecteur vide, la somme est fixée à 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 que la somme reste cohérente.

De cette manière, vous disposez d'une méthode très efficace en O(1) pour "calculer" la somme à tout moment (il suffit de retourner la somme actuellement calculée). L'insertion et la suppression prendront un peu plus de temps car vous ajustez le total et vous devez prendre en compte cette perte de performance.

Les vecteurs où la somme est nécessaire plus souvent que le vecteur n'est modifié sont ceux susceptibles de bénéficier de ce système, étant donné que le coût du calcul de la somme est amorti sur tous les accès. Évidemment, si vous avez seulement besoin de la somme toutes les heures et que le vecteur change trois mille fois par seconde, cela ne sera pas adapté.

Quelque chose comme ceci serait suffisant :

class UberVector:
    private Vector vec
    private int sum

    public UberVector():
        vec = new Vector()
        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.

7 votes

Intéressant, mais faites attention car std::vector n'est pas conçu pour l'héritage.

7 votes

Désolé, j'aurais dû être plus clair - vous pourriez créer votre propre classe avec les mêmes méthodes que vector qui maintient un vecteur en son sein, plutôt que d'être une sous-classe propre (is-a).

0 votes

@paxdiablo: Très bien avec le has-a.

23voto

James McNellis Points 193607

Pourquoi faire la somme en avant quand vous pouvez le faire en arrière? Donné :

std::vector v;     // vecteur à additionner
int sum_of_elements(0); // résultat de l'addition

Nous pouvons utiliser le sous-scriptage, en comptant à rebours :

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

Nous pouvons utiliser le "sous-scriptage" avec vérification de la plage, en comptant à rebours (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::const_reverse_iterator i(v.rbegin()); i != v.rend(); ++i)
    sum_of_elements += *i;

Nous pouvons utiliser des itérateurs avant, en itérant à rebours, dans une boucle for (oh, malin!) :

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

Nous pouvons utiliser accumulate avec des itérateurs inverses :

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

Nous pouvons utiliser for_each avec une expression lambda utilisant des itérateurs inverses :

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

Donc, comme vous pouvez le voir, il y a autant de façons d'additionner le vecteur à rebours que de façons de l'additionner en avant, et certaines d'entre elles sont bien plus passionnantes et offrent bien plus d'opportunités pour des erreurs de décalage d'un élément.

38 votes

Et pourquoi ne pas également parcourir le vecteur en ajoutant un nombre premier avec l'opérateur de modulo pour le retour à la ligne? :-)

3 votes

@paxdiablo Vous avez simplement besoin d'être relativement premier à v.size().

0 votes

Msandiford: non seulement. @paxdiablo: vous devez être relativement premier si vous voulez accéder à tous les éléments. marcher +7 dans un vecteur de taille 7 ne vous donnera pas grand-chose...

17voto

rafak Points 3310
#include
int somme = boost::accumulate(vector, 0);

0 votes

Merci pour la réponse. En passant, quelle est la différence de complexité temporelle entre std::accumulate et boost::accumulate?

1 votes

La complexité temporelle est la même pour accumulate de std et de 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 de réelle différence.

7 votes

boost::accumulate est juste une enveloppe autour de std::accumulate.

5voto

kriss Points 10450

Je suis un utilisateur de Perl, et 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 à combien de façons de trouver la somme des éléments d'un vecteur en C++ est probablement une infinité...

My 2 cents:

Utiliser BOOST_FOREACH, pour se débarrasser de la syntaxe laide de l'itérateur:

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

itérer sur les indices (vraiment facile à lire).

int i, sum = 0;
for (i=0; i

`Celui-ci est destructeur, accédant au vecteur comme une pile:

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

0 votes

Pourquoi dites-vous que l'itération sur des indices est inefficace? Quel est votre fondement pour dire cela?

0 votes

@bobobobo: eh bien, inefficace est probablement excessif. Vous devez à la fois calculer la position des données effective à partir du vecteur et incrémenter le compteur, mais une de ces deux opérations devrait suffire, mais le coût du déréférencement des itérateurs pourrait même être pire. Par conséquent, je vais supprimer le mot.

0 votes

Un compilateur optimisant peut optimiser la variable d'index et simplement utiliser une incrémentation de pointeur s'il le souhaite. (Il peut faire en sorte que la condition de sortie de la boucle soit une comparaison de pointeur contre start + length ). Les itérateurs réels devraient également être optimisés entièrement. N'oubliez pas, ce n'est pas du perl; il est entièrement compilé en langage d'assemblage, pas interprété.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X