У меня есть стандартный контейнер библиотеки больших чисел, таких больших, что они могут вызвать переполнение, если я добавлю их вместе. Давайте представим, что это этот контейнер:
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
?
Вы не должны использовать целое число для хранения результата:
Тип возврата, переданный в функцию скапливаться:
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;
}
Это может не так хорошо, но это работает, даже когда нет 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;
};
Ваше бегущее «среднее» является первым параметром лямбды, поэтому следующее верно.
lambda = [&](int a, int b){return a + b/v.size();};