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