c++ lambda accumulate

c++ - ¿Cómo puedo usar std:: acumular y una lambda para calcular una media?



accumulate (3)

Tengo un contenedor de biblioteca estándar de grandes números, tan grande que pueden causar desbordamiento si los agrego. Vamos a pretender que es este contenedor:

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

Quiero calcular la media de este contenedor, utilizando std :: acumular, pero no puedo sumar todos los números. Simplemente lo calcularé con v[0]/v.size() + v[1]/v.size() + ... Así que me puse:

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

Aquí está lo que he intentado hasta ahora, donde -> indica la salida:

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

¿Cómo puedo producir la media correcta para que la salida sea 5 ?


Es posible que no se redondee tan bien, pero funciona incluso cuando no hay un método size() en el contenedor:

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

Esto aprovecha las nuevas características de C ++ 14, capturas inicializadas , para almacenar el estado dentro de la lambda. (Puede hacer lo mismo a través de la captura de una variable local adicional, pero luego su alcance es el alcance local, en lugar de la vida útil de la lambda). Para versiones anteriores de C ++, naturalmente, solo puede poner el count en la variable miembro de una struct y poner el cuerpo lambda como su operator()() implementación.

Para evitar la acumulación de errores de redondeo (o al menos reducirlo drásticamente), se puede hacer algo como:

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


No debes usar un entero para almacenar el resultado:

El tipo de retorno pasado a la función se accumulate :
T accumulate( InputIt first, InputIt last, T init, BinaryOperation op ); Depende del tercer tipo de parámetro: ( T init ), por lo que debe poner allí: 0.0 para obtener el resultado como doble .

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


Su "promedio" en ejecución es el primer parámetro de la lambda, por lo que lo siguiente es correcto.

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