Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use std::accumulate and a lambda to calculate a mean?

Tags:

I have a standard library container of large numbers, so large that they may cause overflow if I add them together. Let's pretend it's this container:

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

I want to calculate the mean of this container, using std::accumulate, but I can't add all the numbers together. I'll just calculate it with v[0]/v.size() + v[1]/v.size() + .... So I set:

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

Here is what I have tried so far, where -> indicates the output:

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 

How can I produce the correct mean such that the output will be 5?

like image 240
EMBLEM Avatar asked Apr 16 '15 20:04

EMBLEM


People also ask

What does std:: accumulate do?

std::accumulate() is a built-in function in C++'s Standard Template Library. The function takes in a beginning iterator, an ending iterator, initial value, and (by default) computes the sum of the given initial value and the elements in the given range. The function can also​ be used for left folding.

What does [=] mean in lambda function?

The [=] you're referring to is part of the capture list for the lambda expression. This tells C++ that the code inside the lambda expression is initialized so that the lambda gets a copy of all the local variables it uses when it's created.


2 Answers

You shouldn't use integer to store the result:

The return type passed to the function accumulate:
T accumulate( InputIt first, InputIt last, T init, BinaryOperation op ); depends on the third parameter type: (T init) so you have to put there: 0.0 to get result as 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; } 
like image 132
AdamF Avatar answered Oct 29 '22 12:10

AdamF


This may not round quite as nicely, but it works even when there's no size() method on the container:

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

This takes advantage of a new C++14 features, initialized captures, to store state within the lambda. (You can do the same thing via capture of an extra local variable, but then its scope is local scope, rather than lifetime of the lambda.) For older C++ versions, you naturally can just put the count in the member variable of a struct and put the lambda body as its operator()() implementation.

To prevent accumulation of rounding error (or at least dramatically reduce it), one can do something like:

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; }; 
like image 34
Ben Voigt Avatar answered Oct 29 '22 11:10

Ben Voigt