Подтвердить что ты не робот

Как я могу использовать 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?

4b9b3361

Ответ 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