Каков наилучший численный способ расчета среднего

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

Спасибо.


Дополнительная информация: предпочтительны инкрементальные подходы, так как количество значений может не уместиться в ОЗУ (несколько параллельных вычислений на файлах размером более 4 ГБ).


person Tobias Langner    schedule 26.09.2011    source источник
comment
Тот, кто проголосовал за закрытие как неконструктивное, сильно ошибся. Это отличный и правильный вопрос.   -  person David Heffernan    schedule 26.09.2011
comment
Обратите внимание, что различные представленные алгоритмы не исключают друг друга. Вполне возможно прочитать фрагменты размером 1 МБ, отсортировать их, просуммировать, а затем использовать суммирование Кэхана по всем частичным суммам.   -  person MSalters    schedule 26.09.2011
comment
спасибо за все ваши комментарии. Они помогли мне понять мою проблему. Я приму статью в качестве ответа, поскольку в ней представлен анализ различных способов обработки суммы.   -  person Tobias Langner    schedule 28.09.2011


Ответы (6)


Вы можете посмотреть на http://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.43.3535 (Ник Хайэм, "Точность суммирования с плавающей запятой", SIAM Journal of Scientific Computation, 1993).

Если я правильно помню, компенсированное суммирование (суммирование Кахана) хорошо, если все числа положительны, по крайней мере так же хорошо, как их сортировка и сложение в порядке возрастания (если только чисел не очень-очень много). История намного сложнее, если некоторые числа положительные, а некоторые отрицательные, так что вы получаете отмену. В этом случае есть аргумент для добавления их в порядке убывания.

person Jitse Niesen    schedule 26.09.2011
comment
Всегда есть дешевый прием суммирования положительных и отрицательных чисел по отдельности. В этом случае скорость алгоритма не так критична, если она O(N); дисковый ввод-вывод будет доминировать почти при любом количестве операций FP. - person MSalters; 26.09.2011
comment
@MSalters Зачем вам суммировать их отдельно? Если вы хотите свести к минимуму ошибку округления, то промежуточные результаты должны быть как можно меньше (по абсолютной величине). Суммирование их по отдельности дает обратный эффект. - person Jitse Niesen; 28.09.2011
comment
Как вы сами заметили, компенсированное суммирование хорошо, если все числа имеют один и тот же знак. - person MSalters; 29.09.2011
comment
Компенсированное суммирование по-прежнему хорошо работает с положительными и отрицательными условиями. Его ошибка равна cond(S_n)u, где cond(S_n) — номер условия суммирования, а u — единица округления. Регулярное суммирование имеет ошибку ~ncond(S_n)u. Если сумма плохо обусловлена, ни одно из предложений не помогает. - person user14717; 08.05.2020
comment
С другой стороны, эта идея о раздельном суммировании положительных и отрицательных частей все еще имеет проблемы. Вы получаете два точных результата, но все же можете получить катастрофическую отмену в окончательных результатах, когда эти подсуммы примерно равны. - person MSalters; 08.03.2021

Если вам нужен алгоритм O(N), посмотрите на суммирование Кэхана.

person MSalters    schedule 26.09.2011
comment
Очевидно, что O(N) будет быстрее, чем метод сортировки. Знаете ли вы, является ли Кахан более или менее точным, чем сортировка перед суммированием? - person David Heffernan; 26.09.2011
comment
@David Heffernan: из статьи в Википедии: хотя алгоритм Кахана обеспечивает рост ошибки O (1) для суммирования n чисел, только немного худший рост O (logn) может быть достигнут путем попарного суммирования: один рекурсивно делит набор чисел на две половины, суммирует каждую половину, а затем добавляет две суммы. Я сомневаюсь, что метод метода сортировки может выполнить O (1). - person Karoly Horvath; 26.09.2011
comment
@yi_H Я не совсем понимаю логику. Попарное суммирование, как описано там, кажется, не включает сортировку. Неважно, сортировка явно очень дорогая по сравнению с Кэханом или попарным суммированием. - person David Heffernan; 26.09.2011
comment
@David: зависит от точного алгоритма суммирования, который вы используете после сортировки. См. citeseerx.ist.psu.edu/viewdoc / для более подробной информации. Возможна 100% точность (т.е. единственная ошибка связана с конечным объемом памяти для результата) - person MSalters; 26.09.2011

Расположите числа в порядке возрастания величины. Суммируйте их, сначала низкую величину. Разделить на количество.

person David Heffernan    schedule 26.09.2011
comment
Как здесь влияет сортировка и добавление чисел в порядке возрастания? - person Jan S; 26.09.2011
comment
зачем сортировка? разве это не склонно к переполнениям (сумма может переполниться)? - person Tobias Langner; 26.09.2011
comment
@Jan Это уменьшает округление. Подумай об этом. - person David Heffernan; 26.09.2011
comment
@Tobias Переполнение действительно проблема для вас? - person David Heffernan; 26.09.2011
comment
@ Дэвид: да, может быть. Возможно, расчет используется для нескольких миллионов выборок. - person Tobias Langner; 26.09.2011
comment
@Тобиас И что? Это размер образцов. Вам нужно, чтобы отдельные значения были порядка 1e300, чтобы беспокоиться о переполнении. - person David Heffernan; 26.09.2011
comment
@Jan S Округление происходит, когда вы добавляете (или вычитаете) два значения, сильно различающиеся по величине. - person David Heffernan; 26.09.2011
comment
@Jan: простой пример: одно большое число (например, 10 ^ 20) и множество маленьких чисел (например, 1). Если вы поместите 10 ^ 20 в качестве первого числа, добавление 1 ничего не даст, поскольку оно округляется - всегда. Это может существенно изменить сумму. - person Tobias Langner; 26.09.2011
comment
Переполнение является серьезной проблемой, учитывая редактирование вопроса. При суммировании миллиарда удвоений ваша сумма, конечно, в миллиард раз больше, чем в среднем. Это 1E9, что было бы проблемой для поплавков с одинарной точностью (38-9), но обычно не для поплавков с двойной точностью (308-9). - person MSalters; 26.09.2011
comment
@MSalters Да, я предполагал двойную точность. Вы хорошо заметили насчёт сингла. - person David Heffernan; 26.09.2011
comment
Переполнение еще более актуально для чисел с половинной точностью (используемых все больше и больше для графических процессоров с тензорными ядрами). - person isarandi; 13.05.2019

Просто добавьте один возможный ответ для дальнейшего обсуждения:

Постепенно вычисляйте среднее значение для каждого шага:

AVG_n = AVG_(n-1) * (n-1)/n + VALUE_n / n

или попарное сочетание

AVG_(n_a + n_b) = (n_a * AVG_a + n_b * AVG_b) / (n_a + n_b)

(надеюсь формулы достаточно понятны)

person Tobias Langner    schedule 26.09.2011
comment
По общему признанию, я не разрабатывал это сам, но похоже, что повторяющиеся деления инкрементальной формы приведут к большей потере точности. Часть проблемы заключается в том, что 1/n вносит ошибки в младшие значащие биты, поэтому n/n != 1, по крайней мере, когда выполняется как трехэтапная операция (разделить-сохранить-умножить). Это сводится к минимуму, если деление выполняется только один раз, но вы будете делать это с данными в ГБ. - person rcollyer; 06.10.2011
comment
С этой формулой вы не столкнетесь с риском переполнения типа данных. - person Tobias Langner; 08.08.2012
comment
Потрясающие формулы. Вот моя реализация первого на C#: double average(IList<int> numbers, int avgUpTo1BasedIndex) { return (avgUpTo1BasedIndex == 1) ? numbers[0] : average(numbers, avgUpTo1BasedIndex - 1) * (avgUpTo1BasedIndex - 1) / avgUpTo1BasedIndex + (double)numbers[avgUpTo1BasedIndex - 1] / avgUpTo1BasedIndex; } - person ShawnFeatherly; 25.10.2014

Я всегда использую следующий псевдокод:

float mean=0.0; // could use doulbe
int n=0;  // could use long

for each x in data:
    ++n;
    mean+=(x-mean)/n;

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

person Dave    schedule 03.06.2014
comment
Обратите внимание, что для float это будет тем менее точным, чем ближе n будет к 2^23. Например, последовательность из 10 миллионов значений 1.0, за которыми следуют 10 миллионов значений 2.0, в среднем составит 1.269. Это потому, что (x-mean)/n приближается к нулю, а mean не меняется при добавлении. - person jpa; 06.03.2021
comment
@jpa, если вы беспокоитесь об этом, вы всегда можете сначала перетасовать их. - person Dave; 07.03.2021
comment
+1, так как это почти так же просто, как наивная реализация без накопления накопления. Какая книга Кнута, том 2? - person Justin Meiners; 14.04.2021

Очень поздний пост, но, поскольку у меня недостаточно репутации, чтобы комментировать, метод @Dave используется (по состоянию на декабрь 2020 г.) Научная библиотека Gnu.

Вот код, извлеченный из mean_source.c:

double FUNCTION (gsl_stats, mean) (const BASE data[], const size_t stride, const size_t size)
{
/* Compute the arithmetic mean of a dataset using the recurrence relation mean_(n) = mean(n-1) + (data[n] - mean(n-1))/(n+1)   */

long double mean = 0;
size_t i;

for (i = 0; i < size; i++)
{
  mean += (data[i * stride] - mean) / (i + 1);
}

return mean;
}

GSL использует тот же алгоритм для вычисления дисперсии, которая, в конце концов, является просто средним значением квадратов разностей от заданного числа.

person freeB    schedule 20.12.2020