Функция для вычисления среднего значения массива double[] с использованием аккумуляции

Должно быть, это самая распространенная функция, для которой у всех где-то есть фрагмент кода, но на самом деле я потратил не менее 1,5 часов на поиск ее на SO, а также на других сайтах C++ и не нашел решения.

Я хочу вычислить среднее значение double array[] с помощью функции. Я хотел бы передать массив функции в качестве ссылки. Есть миллионы примеров, когда среднее значение вычисляется в цикле main(), но то, что я ищу, — это функция, которую я могу поместить во внешний файл и использовать в любое время позже.

Пока вот моя последняя версия, которая выдает ошибку компиляции:

double mean_array( double array[] )
{
    int count = sizeof( array ) / sizeof( array[0] );
    double sum = accumulate( array, array + count, 0 );
    return ( double ) sum / count;
}

Ошибка компиляции:

ошибка C3861: «накопить»: идентификатор не найден

Не подскажете, как исправить эту функцию? Что означает эта ошибка компиляции?

Если я использую std::accumulate (поверх уже определенного using namespace std), то получаю следующую ошибку:

'accumulate' : is not a member of 'std'
'accumulate': identifier not found

Почему «накопить» не входит в «стандартный»?

p.s.: я знаю, что могу сделать 'sum += array[i]' и не использовать аккумулирование, но я хотел бы понять, что здесь происходит и как заставить мой пример работать.


person hyperknot    schedule 26.10.2011    source источник
comment
Я не уверен, но просто интересно, вы добавили необходимый файл Include?   -  person Shamim Hafiz    schedule 26.10.2011
comment
Будьте осторожны: accumulate( array, array + count, 0.0 ); Обратите внимание на 0.0 — вы накапливаете целые числа в своем фрагменте кода.   -  person Tom    schedule 26.10.2011
comment
Итак, если я начну с 0.0, все будет хорошо? Нужно ли мне передавать количество, или я могу рассчитать его из sizeof?   -  person hyperknot    schedule 26.10.2011
comment
Вы не можете использовать sizeof(array), так как размер массива неизвестен. См. Ответ @xanatos ниже для лучшего способа в этом случае.   -  person Some programmer dude    schedule 26.10.2011
comment
Кстати, здесь удобно использовать std::accumulate. Если ваш массив очень большой, его можно тривиально распараллелить с помощью for_example, __gnu_parallel::accumulate.   -  person Tom    schedule 26.10.2011


Ответы (4)


Попробуйте добавить

#include <numeric>

Это принесет функцию 'std::accumulate', которую вы ищете.

Идя дальше, у вас возникнет проблема узнать количество элементов в вашем массиве. Действительно, массив нельзя передать функции в надежде, что функция сможет узнать размер массива. Он распадется на указатель. Следовательно, ваш расчет count будет неправильным. Если вы хотите иметь возможность передать массив указанного фактического размера, вам нужно использовать шаблонную функцию.

template <int N>
double mean_array( double ( & array )[N] )
{
    return std::accumulate( array, array + N, 0.0) / (double)(N);
}
person Didier Trosset    schedule 26.10.2011
comment
Я не уверен, можно ли вызвать эту шаблонную функцию в методе (оболочке) Objective-C. @didier-trosset: Не могли бы вы уточнить, как шаблон помогает при подсчете? Если мне придется вызывать функцию C++ из Objective-C, где у меня уже есть счетчик, согласитесь ли вы с тем, что шаблоны больше не потребуются, поскольку счетчик будет передан в качестве второго аргумента этой функции? - person Kushal Ashok; 26.02.2018
comment
@KushalAshok Правильно. Либо вы передаете указатель и счетчик функции без шаблона; или вы передаете только массив шаблонной функции (которая будет угадывать количество). - person Didier Trosset; 27.02.2018
comment
спасибо за подтверждение. Я передал указатель и подсчитал и не смог найти никаких утечек памяти при работе с инструментами. - person Kushal Ashok; 28.02.2018

Это не совсем тот вопрос, который вы задали, но в вашем примере кода есть простая ошибка. Начальное значение в accumulate шаблонно, а в вашем коде оно шаблонно для целых чисел. Если вы передадите ему набор двойников, они будут преобразованы в целые числа, и вы получите неправильные ответы. Совершив эту ошибку раньше, я сделал себе быструю гарантию следующим образом:

  /** Check that not inputting integer type into accumulate
   *  This is considered an error in this program (where a double was expected
   *  @tparam InputIterator The iterator to accumulate
   *  @tparam T The type to accumulate - will fail if integer.
   *  @param first The first iterator to accumulate from.
   *  @param last the iterator to acculate to,
   *  @param init The initial value
   *  @return The accumulated value as evaluated by std::accumulate.
   */
  template<class InputIterator, class T>
  inline
  T
  accumulate_checked(InputIterator first, InputIterator last, T init )
  {
    return std::accumulate(first,last, init);
  }

  //Not implemented for integers (will not compile if called).
  template<class InputIterator>
  inline
  int
  accumulate_checked(InputIterator first, InputIterator last, int init );

Решил поделиться, если интересно.

Просто для полноты ваша функция может выглядеть так:

double mean_array( double *array, size_t count )
{
    double sum = std::accumulate(array,array+count,0.0)
    return sum / count;
}

или быть особенно осторожным

double mean_array( double *array, size_t count )
{
    double sum = accumulate_checked(array,array+count,0.0)
    return sum / count;
}

или еще лучше шаблонную версию от Didier Trosset

person Tom    schedule 26.10.2011

Чтобы использовать std::accumulate, вам нужно включить соответствующий заголовок. Добавьте следующее в исходный файл.

#include <numeric>
person Blastfurnace    schedule 26.10.2011
comment
Хорошо, кажется, это тот, кого мне не хватало. У меня много включений, я пропустил это. - person hyperknot; 26.10.2011

double mean_array( double *array, size_t count )
{
    double sum = 0.0;

    for (size_t i = 0; i < count; i++)
    {
        sum += array[i];
    }

    return sum / count;
}

or

double mean_array( double *array, size_t count )
{
    double sum = 0.0;
    double *pastLast = array + count;

    while (array < pastLast)
    {
        sum += *array;
        array++;
    }

    return sum / count;
}

Если вы передаете массив функции, вы «теряете» его размер, поэтому вам нужно передать его как параметр (это немного сложнее, чем это... но пока этого должно быть достаточно)

person xanatos    schedule 26.10.2011
comment
Но я не понимаю, разве это не работает с int count = sizeof( array ) / sizeof( array[0] ); ? - person hyperknot; 26.10.2011
comment
Какая часть If you pass an array to a function, you "lose" its size, so you have to pass it as a parameter непонятна? sizeof(array) is == sizeof(double*) в этот момент. Размер массива теряется, если вы передаете его функции. Если вы мне не доверяете, попробуйте отладить его. - person xanatos; 26.10.2011
comment
@zsero: когда вы передаете массив в качестве параметра функции, он превращается в указатель. Это больше не тип массива и теряет размер. Как правило, вы также передаете размер в качестве отдельного параметра, как в примерах xanatos. - person Blastfurnace; 26.10.2011
comment
Хорошо, теперь понятно. Я не знал, что он «распадается» до указателя. Спасибо за код и объяснение. - person hyperknot; 26.10.2011