Как я могу использовать std:: accumulate и лямбда для вычисления среднего?
У меня есть стандартный библиотечный контейнер больших чисел, настолько большой, что они могут вызвать переполнение, если я их добавлю. Пусть притворите этот контейнер:
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
Я хочу рассчитать среднее значение этого контейнера, используя std:: accumulate, но я не могу добавить все числа вместе. Я просто вычислил его с помощью 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
?
Ответы
Ответ 1
Вы не должны использовать целое число для хранения результата:
Тип возврата, переданный функции накапливать:
T accumulate( InputIt first, InputIt last, T init, BinaryOperation op );
зависит от третьего типа параметра: (T init), поэтому вы должны поместить туда: 0.0, чтобы получить результат как 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;
}
Ответ 2
Это может быть не совсем так хорошо, но работает, даже если в контейнере нет метода size()
:
auto lambda = [count = 0](double a, int b) mutable { return a + (b-a)/++count; };
Это позволяет использовать новые возможности С++ 14, инициализированные записи, для хранения состояния в лямбда. (Вы можете сделать то же самое с помощью захвата дополнительной локальной переменной, но тогда ее область видимости - это локальная область, а не время жизни лямбда.) Для более старых версий С++ вы, естественно, можете просто поместить count
в переменную-член a 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;
};
Ответ 3
Ваш текущий "средний" является первым параметром для лямбда, поэтому следующее верно.
lambda = [&](int a, int b){return a + b/v.size();};
Ответ 4
Три лямбда-функции, которые вы используете, не на высоте.
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
Используемый здесь параметр a переносит среднее значение до определенного индекса вектора в данный момент времени. Например, значение "a", когда значение "b" равно 1, равно 0,0, когда "b" становится равным 2 в этот момент оно должно быть "0,1". Тогда совершенно ясно, что ни в коем случае не нужно делить a на v.size() каждый раз, когда вызывается лямбда-функция.
Приходя к правильной лямбда-функции для упомянутой ситуации
lambda = [&](double x,double y){return x+y/v.size();}
Здесь мы берем ссылку только потому, что нам нужно значение v.size(), значение размера вектора может быть передано заранее, если известно заранее
Рабочая программа
#include<iostream>
#include<numeric>
#include<vector>
using namespace std;
int main(){
vector<int> v(10);
iota(v.begin(),v.end(),1);
double x=accumulate(v.begin(),v.end(),0.0,[&](double x,double y) {return x+y/v.size();});
cout << x << endl;
}
PS: "йота" используется для увеличения диапазона в порядке возрастания, здесь он инициализирует вектор от 1 до 10