285 votes

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

Quelles sont les bonnes méthodes pour 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 manière de le faire.

3 votes

Que voulez-vous dire lorsque vous dites "fonction similaire à ?" Cherchez-vous 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 aussi qu'il soit différent à certains égards (sinon vous pourriez simplement utiliser std::accumulate) ; quel(s) différence(s) par rapport à std::accumulate recherchez-vous?

482voto

Prasoon Saurav Points 47488

En fait, il existe 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 là-bas, il accumulera des int même si le vecteur contient des float. Si vous additionnez des nombres à virgule flottante, changez 0 en 0.0 ou 0.0f (merci à nneonneo). Voir aussi la solution C++11 ci-dessous.

C++11 et au-delà

  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 en utilisant les plages (merci à Roger Pate) :

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

C++17 et plus

  1. Utilisation de std::reduce qui prend également en charge le type de résultat, par exemple si vous avez std::vector, vous obtenez int comme résultat. Si vous avez std::vector, vous obtenez float. Ou si vous avez std::vector, vous obtenez 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 existe 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 le résultat rapidement.

7 votes

Bien sûr, en C++03, vous pouvez utiliser std :: for_each avec un foncteur, il suffit juste de plus de lignes de code pour le définir que le 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 accumuler à l'intérieur de for_each mais n'est-ce pas un 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à proposé toute une série de façons différentes (et bonnes) de le faire, dont aucune n'a besoin d'être répétée ici. J'aimerais proposer cependant une approche alternative pour la vitesse.

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 vraiment sous-classer vector qui est incertain en raison du manque d'un destructeur virtuel - je parle plutôt d'une classe qui contient la somme et un vecteur en elle, has-a plutô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, toute modification du vecteur sous-jacent est interceptée pour garantir que la somme reste cohérente.

De cette façon, vous disposez d'une méthode très efficace 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 changé sont ceux susceptibles de bénéficier de ce schéma, étant donné que le coût du calcul de la somme est amorti sur tous les accès. Évidemment, si vous avez uniquement 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 suffirait :

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 destiné à la sous-classe.

7 votes

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

0 votes

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

23voto

James McNellis Points 193607

Pourquoi effectuer la sommation vers l'avant lorsque vous pouvez le faire en arrière? Étant donné :

std::vector v;     // vecteur à sommer
int sum_of_elements(0); // résultat de la sommation

Nous pouvons utiliser le subscipting, en comptant à rebours :

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

Nous pouvons utiliser le "subscripting" vérifié, 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 avancés, en itérant à l'envers, dans une boucle for (oh, délicat!) :

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 en 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 existe tout autant de façons de sommer le vecteur en arrière que de le sommer en avant, et certaines de ces façons sont beaucoup plus excitantes et offrent beaucoup plus d'opportunités pour des erreurs d'un élément.

38 votes

Et pourquoi ne pas aussi parcourir le vecteur en ajoutant un nombre premier avec l'opérateur de modulo pour l'enroulement? :-)

3 votes

@paxdiablo Vous devez simplement être relativement premier à v.size().

0 votes

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

17voto

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

0 votes

Merci pour la réponse. Au fait, quelle est la différence en termes de complexité temporelle entre std::accumulate et boost::accumulate ?

1 votes

La complexité temporelle est la même pour std et boost accumulate - linéaire. Dans ce cas, boost::accumulate est juste plus facile à taper que de transmettre 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 façons différentes 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é...

Mon avis :

En utilisant BOOST_FOREACH, pour se débarrasser de la syntaxe moche 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 destructif, en 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 les 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 effective des données à partir du vecteur et incrémenter le compteur, mais l'une de ces deux opérations devrait suffire, mais le coût de la déréférenciation des itérateurs peut même être pire. Par conséquent, je vais supprimer le mot.

0 votes

Un compilateur d'optimisation 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 boucle soit une comparaison de pointeur contre start + length). Les itérateurs réels devraient également être totalement optimisés. Rappelez-vous, ce n'est pas perl ; il est entièrement compilé en asm, 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