web-dev-qa-db-fra.com

Comment utiliser std :: accumulate et un lambda pour calculer une moyenne?

J'ai un conteneur de bibliothèque standard de grands nombres, si grands qu'ils peuvent provoquer un débordement si je les additionne. Imaginons que c'est ce conteneur:

std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

Je veux calculer la moyenne de ce conteneur en utilisant std :: accumulate, mais je ne peux pas additionner tous les nombres. Je vais juste le calculer avec v[0]/v.size() + v[1]/v.size() + .... Alors j'ai mis:

auto lambda = ...;
std::cout << std::accumulate(v.begin(), v.end(), 0, lambda) << std::endl;

Voici ce que j'ai essayé jusqu'à présent, où -> indique la sortie:

lambda = [&](int a, int b){return (a + b)/v.size();};  ->  1
lambda = [&](int a, int b){return a/v.size() + b/v.size();};  ->  1
lambda = [&](int a, int b){return a/v.size() + b;};  ->  10

Comment puis-je produire la moyenne correcte telle que la sortie sera 5?

17
EMBLEM

Vous ne devriez pas utiliser un entier pour stocker le résultat:

Le type de retour passé à la fonction accumulate :
T accumulate( InputIt first, InputIt last, T init, BinaryOperation op ); dépend du troisième type de paramètre: ( T init ), vous devez donc y mettre: 0.0 pour obtenir le résultat sous la forme double .

#include <vector>
#include <algorithm>
#include <iostream>
#include <numeric>
using namespace std;
std::vector<int> v = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

int main()
{
    auto lambda = [&](double a, double b){return a + b / v.size(); };
    std::cout << std::accumulate(v.begin(), v.end(), 0.0, lambda) << std::endl;
}
17
AdamF

Cela peut ne pas être aussi efficace, mais cela fonctionne même s'il n'y a pas de méthode size() sur le conteneur:

auto lambda = [count = 0](double a, int b) mutable { return a + (b-a)/++count; };

Ceci tire parti des nouvelles fonctionnalités C++ 14, captures initialisées , pour stocker l’état dans le lambda. (Vous pouvez faire la même chose en capturant une variable locale supplémentaire, mais sa portée est alors la portée locale plutôt que la durée de vie du lambda.) Pour les anciennes versions de C++, vous pouvez naturellement simplement mettre la count dans la variable membre d'un struct et mettez le corps lambda comme implémentation operator()().

Pour éviter l’accumulation d’erreurs d’arrondi (ou au moins la réduire considérablement), on peut faire quelque chose comme:

auto lambda = [count = 0, error = 0.0](double a, int b) mutable {
   const double desired_change = (b-a-error)/++count;
   const double newa = a + (desired_change + error);
   const double actual_change = newa - a;
   error += desired_change - actual_change;
   return newa;
};
7
Ben Voigt

Votre "moyenne" en cours d'exécution est le premier paramètre du lambda, ce qui suit est correct.

lambda = [&](int a, int b){return a + b/v.size();};
0
Ken Bloom