Как я могу использовать std :: накопить и лямбда для вычисления среднего?

У меня есть стандартный контейнер библиотеки больших чисел, таких больших, что они могут вызвать переполнение, если я добавлю их вместе. Давайте представим, что это этот контейнер:

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

Я хочу вычислить среднее значение этого контейнера, используя std :: аккумулировать, но я не могу сложить все числа вместе. Я просто вычислю это с v[0]/v.size() + v[1]/v.size() + ..., Итак, я установил:

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

Вот что я уже пробовал, где -> указывает на вывод:

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

Как я могу произвести правильное среднее значение, так что результат будет 5?

17

Решение

Вы не должны использовать целое число для хранения результата:

Тип возврата, переданный в функцию скапливаться:
T accumulate( InputIt first, InputIt last, T init, BinaryOperation op ); зависит от третьего типа параметра 🙁T init) поэтому вы должны поставить туда: 0.0, чтобы получить результат как двойной.

#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

Другие решения

Это может не так хорошо, но это работает, даже когда нет size() метод на контейнере:

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

Это использует новые возможности C ++ 14, инициализированные захваты, хранить состояние в лямбде. (Вы можете сделать то же самое с помощью захвата дополнительной локальной переменной, но тогда ее областью является локальная область, а не время жизни лямбды.) Для более старых версий C ++ вы, естественно, можете просто поместить count в переменной члена struct и положить лямбда-тело как его operator()() реализация.

Чтобы предотвратить накопление ошибки округления (или хотя бы значительно уменьшить ее), можно сделать что-то вроде:

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

Ваше бегущее «среднее» является первым параметром лямбды, поэтому следующее верно.

lambda = [&](int a, int b){return a + b/v.size();};
0
По вопросам рекламы [email protected]