Jump to content

Алгоритм суммирования Кахана

В численном анализе алгоритм суммирования Кахана , также известный как компенсированное суммирование , [1] значительно уменьшает числовую ошибку в сумме, полученной путем сложения последовательности конечной точности чисел с плавающей запятой , по сравнению с очевидным подходом. Это достигается за счет сохранения отдельной текущей компенсации (переменной для накопления небольших ошибок), что фактически увеличивает точность суммы за счет точности переменной компенсации.

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

Алгоритм ; приписывается Кахану Уильяму [3] Иво Бабушка , похоже, независимо придумал аналогичный алгоритм (отсюда и суммирование Кахана-Бабушки ). [4] Аналогичными, более ранними методами являются, например, линейный алгоритм Брезенхэма , отслеживающий накопленную ошибку в целочисленных операциях (хотя впервые документированный примерно в то же время). [5] ) и дельта-сигма модуляция . [6]

Алгоритм [ править ]

В псевдокоде алгоритм будет такой:

function KahanSum(input)
    // Prepare the accumulator.
    var sum = 0.0
    // A running compensation for lost low-order bits.
    var c = 0.0
    // The array input has elements indexed input[1] to input[input.length].
    for i = 1 to input.length do
        // c is zero the first time around.
        var y = input[i] - c
        // Alas, sum is big, y small, so low-order digits of y are lost.         
        var t = sum + y
        // (t - sum) cancels the high-order part of y;
        // subtracting y recovers negative (low part of y)
        c = (t - sum) - y
        // Algebraically, c should always be zero. Beware
        // overly-aggressive optimizing compilers!
        sum = t
    // Next time around, the lost low part will be added to y in a fresh attempt.
    next i

    return sum

Этот алгоритм также можно переписать для использования алгоритма Fast2Sum : [7]

function KahanSum2(input)
    // Prepare the accumulator.
    var sum = 0.0
    // A running compensation for lost low-order bits.
    var c = 0.0
    // The array input has elements indexed 
    for i = 1 to input.length do
        // c is zero the first time around.
        var y = input[i] + c
        // sum + c is an approximation to the exact sum.
        (sum,c) = Fast2Sum(sum,y)
    // Next time around, the lost low part will be added to y in a fresh attempt.
    next i
    return sum

Рабочий пример [ править ]

Алгоритм не требует какого-либо конкретного выбора системы счисления , только для арифметики «нормализации сумм с плавающей запятой перед округлением или усечением». [3] Компьютеры обычно используют двоичную арифметику, но, чтобы пример было легче читать, он будет дан в десятичном формате. Предположим, мы используем шестизначную десятичную арифметику с плавающей запятой , sum достиг значения 10000,0, а следующие два значения input[i] составляют 3,14159 и 2,71828. Точный результат — 10005,85987, который округляется до 10005,9. При простом суммировании каждое входящее значение будет сопоставлено с sum, и многие младшие цифры будут потеряны (путем усечения или округления). Первый результат после округления будет 10003,1. Второй результат будет 10005,81828 до округления и 10005,8 после округления. Это неправильно.

Однако при компенсированном суммировании мы получаем правильно округленный результат 10005,9.

Предположим, что c имеет начальное значение нулевое. Завершающие нули показаны там, где они имеют значение для шестизначного числа с плавающей запятой.

  y = 3.14159 - 0.00000             y = input[i] - c
  t = 10000.0 + 3.14159             t = sum + y
    = 10003.14159                   Normalization done, next round off to six digits.
    = 10003.1                       Few digits from input[i] met those of sum. Many digits have been lost!
  c = (10003.1 - 10000.0) - 3.14159 c = (t - sum) - y  (Note: Parenthesis must be evaluated first!)
    = 3.10000 - 3.14159             The assimilated part of y minus the original full y.
    = -0.0415900                    Because c is close to zero, normalization retains many digits after the floating point. 
sum = 10003.1                       sum = t

Сумма настолько велика, что накапливаются только старшие цифры входных чисел. Но на следующем этапе c, аппроксимация текущей ошибки, решает проблему.

  y = 2.71828 - (-0.0415900)        Most digits meet, since c is of a size similar to y.
    = 2.75987                       The shortfall (low-order digits lost) of previous iteration successfully reinstated.
  t = 10003.1 + 2.75987             But still only few meet the digits of sum.
    = 10005.85987                   Normalization done, next round to six digits.
    = 10005.9                       Again, many digits have been lost, but c helped nudge the round-off.
  c = (10005.9 - 10003.1) - 2.75987 Estimate the accumulated error, based on the adjusted y.
    = 2.80000 - 2.75987             As expected, the low-order parts can be retained in c with no or minor round-off effects.
    = 0.0401300                     In this iteration, t was a bit too high, the excess will be subtracted off in next iteration.
sum = 10005.9                       Exact result is 10005.85987, sum is correct, rounded to 6 digits.

Алгоритм выполняет суммирование с двумя аккумуляторами: sum содержит сумму, и c накапливает части, не ассимилированные в sum, чтобы подтолкнуть младшую часть sum в следующий раз. Таким образом, суммирование продолжается со «защитными цифрами» в c, что лучше, чем его отсутствие, но не так хорошо, как выполнение вычислений с двойной точностью ввода. Однако простое повышение точности вычислений в целом непрактично; если input уже используется двойная точность, лишь немногие системы поддерживают четырехкратную точность , а если бы и были, input тогда может быть с четырехкратной точностью.

Точность [ править ]

Для оценки точностных характеристик необходим тщательный анализ ошибок компенсированного суммирования. Хотя он более точен, чем простое суммирование, он все же может давать большие относительные ошибки для плохо обусловленных сумм.

Предположим, что кто-то суммирует ценности , для . Точная сумма

(вычислено с бесконечной точностью).

При компенсированном суммировании вместо этого получаем , где ошибка ограничен [2]

где — это машинная точность используемой арифметики (например, для стандарта IEEE двойной точности с плавающей запятой ). Обычно интересующей величиной является относительная ошибка , который, следовательно, ограничен сверху

В выражении для границы относительной ошибки дробь число обусловленности задачи суммирования. По сути, число обусловленности представляет собой внутреннюю чувствительность задачи суммирования к ошибкам, независимо от того, как оно вычисляется. [8] Относительная граница ошибки для каждого ( обратно стабильного ) метода суммирования с использованием фиксированного алгоритма с фиксированной точностью (т.е. не тех, которые используют арифметику произвольной точности или алгоритмов, требования к памяти и времени которых меняются в зависимости от данных), пропорциональна этому числу обусловленности. . [2] Плохо обусловленная задача суммирования — это задача, в которой это отношение велико, и в этом случае даже компенсированное суммирование может иметь большую относительную ошибку. Например, если слагаемые являются некоррелированными случайными числами с нулевым средним значением, сумма представляет собой случайное блуждание , а число обусловленности будет расти пропорционально . С другой стороны, для случайных входных данных с ненулевым значением число обусловленности асимптотируется до конечной константы как . Если все входные данные неотрицательны , то номер условия равен 1.

Учитывая число обусловленности, относительная ошибка компенсированного суммирования фактически не зависит от . В принципе, есть который растет линейно с , но на практике этот член фактически равен нулю: поскольку конечный результат округляется до точности , срок округляется до нуля, если только примерно или больше. [2] В двойной точности это соответствует примерно , намного больше, чем большинство сумм. Итак, при фиксированном числе обусловленности ошибки компенсированного суммирования фактически равны , независимо от .

Для сравнения, относительная ошибка, связанная с простым суммированием (простым последовательным добавлением чисел и округлением на каждом шаге), растет как умноженное на число условий. [2] Однако на практике эта ошибка в худшем случае наблюдается редко, поскольку она возникает только в том случае, если все ошибки округления имеют одно и то же направление. На практике гораздо более вероятно, что ошибки округления имеют случайный знак с нулевым средним значением и образуют случайное блуждание; в этом случае наивное суммирование имеет среднеквадратичную относительную ошибку, которая растет как умноженное на число условий. [9] Однако это все равно гораздо хуже, чем компенсированное суммирование. Однако если сумму можно выполнить с удвоенной точностью, то заменяется на , а наивное суммирование имеет ошибку в худшем случае, сравнимую с ошибкой член в компенсированном суммировании с исходной точностью.

К тому же, который появляется в Выше приведена граница наихудшего случая, которая возникает только в том случае, если все ошибки округления имеют одинаковый знак (и имеют максимально возможную величину). [2] На практике более вероятно, что ошибки имеют случайный знак, и в этом случае члены заменяются случайным блужданием, и в этом случае даже для случайных входных данных с нулевым средним значением ошибка растет только по мере (игнорируя срок), той же ставкой сумма растет, аннулируя факторы при вычислении относительной ошибки. Таким образом, даже для асимптотически плохо обусловленных сумм относительная ошибка компенсированного суммирования часто может быть намного меньше, чем можно было бы предположить при анализе наихудшего случая.

Дальнейшие улучшения [ править ]

Ноймайер [10] представил улучшенную версию алгоритма Кахана, которую он называет «улучшенным алгоритмом Кахана – Бабушки», который также охватывает случай, когда следующий добавляемый термин больше по абсолютному значению, чем текущая сумма, эффективно меняя роль того, что является большим. и что такое маленькое. В псевдокоде алгоритм такой:

function KahanBabushkaNeumaierSum(input)
    var sum = 0.0
    var c = 0.0                       // A running compensation for lost low-order bits.

    for i = 1 to input.length do
        var t = sum + input[i]
        if |sum| >= |input[i]| then
            c += (sum - t) + input[i] // If sum is bigger, low-order digits of input[i] are lost.
        else
            c += (input[i] - t) + sum // Else low-order digits of sum are lost.
        endif
        sum = t
    next i

    return sum + c                    // Correction only applied once in the very end.

Это усовершенствование аналогично замене Fast2Sum на 2Sum в алгоритме Кахана, переписанном с помощью Fast2Sum.

Для многих последовательностей чисел оба алгоритма согласуются, но простой пример принадлежит Питерсу. [11] показывает, чем они могут отличаться. Для подведения итогов в двойной точности алгоритм Кахана дает 0,0, тогда как алгоритм Ноймайера дает правильное значение 2,0.

Возможны также модификации более высокого порядка с большей точностью. Например, вариант, предложенный Кляйном, [12] который он назвал «итерационным алгоритмом Кахана – Бабушки» второго порядка. В псевдокоде алгоритм такой:

function KahanBabushkaKleinSum(input)
    var sum = 0.0
    var cs  = 0.0
    var ccs = 0.0
    var c   = 0.0
    var cc  = 0.0

    for i = 1 to input.length do
        var t = sum + input[i]
        if |sum| >= |input[i]| then
            c = (sum - t) + input[i]
        else
            c = (input[i] - t) + sum
        endif
        sum = t
        t = cs + c
        if |cs| >= |c| then
            cc = (cs - t) + c
        else
            cc = (c - t) + cs
        endif
        cs = t
        ccs = ccs + cc
    end loop 

    return sum + cs + ccs

Альтернативы [ править ]

Хотя алгоритм Кахана достигает рост ошибки при суммировании n чисел, лишь немного хуже роста можно достичь путем попарного суммирования : рекурсивно делят на две половины, суммируют каждую половину, а затем складывают две суммы. набор чисел [2] Преимущество этого подхода состоит в том, что он требует того же количества арифметических операций, что и простое суммирование (в отличие от алгоритма Кахана, который требует в четыре раза больше арифметических операций и имеет задержку в четыре раза больше, чем простое суммирование), и может вычисляться параллельно. Базовый случай рекурсии в принципе может представлять собой сумму только одного (или нуля) чисел, но для амортизации накладных расходов на рекурсию обычно используется больший базовый случай. Эквивалент парного суммирования используется во многих алгоритмах быстрого преобразования Фурье (БПФ) и отвечает за логарифмический рост ошибок округления в этих БПФ. [13] На практике при ошибках округления случайных знаков среднеквадратические ошибки попарного суммирования фактически растут как . [9]

Другая альтернатива — использовать арифметику произвольной точности , которая в принципе вообще не требует округления и требует гораздо больших вычислительных усилий. Способ правильного округления сумм с произвольной точностью состоит в адаптивном расширении с использованием нескольких компонентов с плавающей запятой. Это сведет к минимуму вычислительные затраты в обычных случаях, когда высокая точность не требуется. [14] [11] Другой метод, использующий только целочисленную арифметику, но большой аккумулятор, был описан Киршнером и Кулишом ; [15] аппаратная реализация была описана Мюллером, Рюбом и Рюллингом. [16]

Возможная недействительность из-за оптимизации компилятора [ править ]

В принципе, достаточно агрессивный оптимизирующий компилятор может разрушить эффективность суммирования Кахана: например, если компилятор упростит выражения в соответствии с правилами ассоциативности вещественной арифметики, он может «упростить» второй шаг последовательности

t = sum + y;
c = (t - sum) - y;

к

c = ((sum + y) - sum) - y;

а затем

c = 0;

тем самым устраняя компенсацию ошибки. [17] На практике многие компиляторы не используют правила ассоциативности (которые являются лишь приблизительными в арифметике с плавающей запятой) при упрощениях, если только это явно не указано в параметрах компилятора, разрешающих «небезопасные» оптимизации. [18] [19] [20] [21] хотя компилятор Intel C++ является одним из примеров, который по умолчанию допускает преобразования на основе ассоциативности. [22] Первоначальная K&R C версия языка программирования C позволяла компилятору переупорядочивать выражения с плавающей запятой в соответствии с правилами ассоциативности вещественной арифметики, но последующий стандарт ANSI C запрещал переупорядочение, чтобы сделать C более подходящим для числовых приложений ( и больше похоже на Фортран , который тоже запрещает переупорядочение), [23] хотя на практике параметры компилятора могут снова включить переупорядочение, как упоминалось выше.

Портативный способ локально запретить такую ​​оптимизацию — разбить одну из строк исходной формулировки на два утверждения и сделать два промежуточных продукта нестабильными :

function KahanSum(input)
    var sum = 0.0
    var c = 0.0

    for i = 1 to input.length do
        var y = input[i] - c
        volatile var t = sum + y
        volatile var z = t - sum
        c = z - y
        sum = t
    next i

    return sum

Поддержка библиотеками [ править ]

В общем, встроенные функции «суммирования» в компьютерных языках обычно не дают гарантий того, что будет использован конкретный алгоритм суммирования, не говоря уже о суммировании Кахана. [ нужна ссылка ] Стандарт BLAS для подпрограмм линейной алгебры явно избегает обязательного указания какого-либо конкретного порядка вычислений по соображениям производительности. [24] и реализации BLAS обычно не используют суммирование Кахана.

Стандартная библиотека компьютерного языка Python определяет функцию fsum для точного суммирования. Начиная с Python 3.12, встроенная функция sum() использует суммирование Ноймайера. [25]

В языке Julia реализация по умолчанию sum функция выполняет попарное суммирование для высокой точности и хорошей производительности, [26] но внешняя библиотека обеспечивает реализацию варианта Ноймайера с именем sum_kbn для случаев, когда необходима более высокая точность. [27]

На C# языке пакет Nuget HPCsharp реализует вариант Ноймайера и попарное суммирование : как скалярное, так и параллельное по данным с использованием инструкций процессора SIMD , и параллельное многоядерное суммирование. [28]

См. также [ править ]

Ссылки [ править ]

  1. ^ Строго говоря, существуют и другие варианты компенсированного суммирования: см. Хайэм, Николас (2002). Точность и устойчивость численных алгоритмов (2-е изд.) . СИАМ. стр. 110–123. ISBN  978-0-89871-521-7 .
  2. Перейти обратно: Перейти обратно: а б с д и ж г час Хайэм, Николас Дж. (1993), «Точность суммирования с плавающей запятой», SIAM Journal on Scientific Computing , 14 (4): 783–799, Бибкод : 1993SJSC...14..783H , CiteSeerX   10.1.1.43.3535 , doi : 10.1137/0914050 , S2CID   14071038 .
  3. Перейти обратно: Перейти обратно: а б Кахан, Уильям (январь 1965 г.), «Дальнейшие замечания по уменьшению ошибок усечения» (PDF) , Communications of ACM , 8 (1): 40, doi : 10.1145/363707.363723 , S2CID   22584810 , заархивировано из оригинала (PDF) на 9 февраль 2018 года .
  4. ^ Бабушка, И.: Численная устойчивость в математическом анализе. Инф. Учеб. ˇ 68, 11–23 (1969)
  5. ^ Брезенхэм, Джек Э. (январь 1965 г.). «Алгоритм компьютерного управления цифровым плоттером» (PDF) . IBM Systems Journal . 4 (1): 25–30. дои : 10.1147/sj.41.0025 . S2CID   41898371 .
  6. ^ Иносе, Х.; Ясуда, Ю.; Мураками, Дж. (сентябрь 1962 г.). «Система телеметрии с помощью кодовых манипуляций - ΔΣ-модуляция». Сделки ИРЭ по космической электронике и телеметрии . СЭТ-8: 204–209. дои : 10.1109/IRET-SET.1962.5008839 . S2CID   51647729 .
  7. ^ Мюллер, Жан-Мишель; Бруни, Николас; из Динешена, Флоран; Жаннерод, Клод-Пьер; Джолдес, Миоара; Лефевр, Винсент; Мелькионд, Гийом; Револь, Натали ; Торрес, Серж (2018) [2010]. Справочник по арифметике с плавающей запятой (2-е изд.). Биркхойзер . п. 179. дои : 10.1007/978-3-319-76526-6 . ISBN  978-3-319-76525-9 . LCCN   2018935254 .
  8. ^ Трефетен, Ллойд Н .; Бау, Дэвид (1997). Численная линейная алгебра . Филадельфия: СИАМ. ISBN  978-0-89871-361-9 .
  9. Перейти обратно: Перейти обратно: а б Манфред Таше и Хансмартин Зойнер, Справочник по аналитически-вычислительным методам в прикладной математике , Бока-Ратон, Флорида: CRC Press, 2000.
  10. ^ Ноймайер, А. (1974). « Анализ ошибок округления некоторых методов суммирования конечных сумм » (PDF) . Журнал прикладной математики и механики (на немецком языке). 54 (1): 39–51. Стартовый код : 1974ЗаММ...54...39Н . дои : 10.1002/замм.19740540106 .
  11. Перейти обратно: Перейти обратно: а б Хеттингер, Р. «Повышение точности встроенной функции sum() для входных данных с плавающей запятой · Проблема № 100425 · python/cpython» . GitHub — Добавлены функции CPython v3.12 . Проверено 7 октября 2023 г.
  12. ^ А., Кляйн (2006). «Обобщенный алгоритм суммирования Кахана – Бабушки». Вычисление . 76 (3–4). Спрингер-Верлаг: 279–293. дои : 10.1007/s00607-005-0139-x . S2CID   4561254 .
  13. ^ Джонсон, СГ; Фриго, ведущий Сидни Бернс (ред.). «Быстрые преобразования Фурье: реализация БПФ на практике» . Архивировано из оригинала 20 декабря 2008 г.
  14. ^ Ричард Шевчук, Джонатан (октябрь 1997 г.). «Адаптивная точная арифметика с плавающей запятой и быстрые устойчивые геометрические предикаты» (PDF) . Дискретная и вычислительная геометрия . 18 (3): 305–363. дои : 10.1007/PL00009321 . S2CID   189937041 .
  15. ^ Киршнер, Р.; Кулиш, У. (июнь 1988 г.). «Точная арифметика для векторных процессоров» . Журнал параллельных и распределенных вычислений . 5 (3): 250–270. дои : 10.1016/0743-7315(88)90020-2 .
  16. ^ Мюллер, М.; Руб, К.; Руллинг, В. (1991). Точное накопление чисел с плавающей запятой . Материалы 10-го симпозиума IEEE по компьютерной арифметике. стр. 64–69. дои : 10.1109/ARITH.1991.145535 .
  17. ^ Голдберг, Дэвид (март 1991 г.), «Что каждый ученый-компьютерщик должен знать об арифметике с плавающей запятой» (PDF) , ACM Computing Surveys , 23 (1): 5–48, doi : 10.1145/103162.103163 , S2CID   222008826 .
  18. ^ Руководство по коллекции компиляторов GNU , версия 4.4.3: 3.10 Параметры, управляющие оптимизацией , -fassociative-math (21 января 2010 г.).
  19. ^ Руководство пользователя Compaq Fortran для систем Tru64 UNIX и Linux Alpha. Архивировано 7 июня 2011 г. на Wayback Machine , раздел 5.9.7 Оптимизация арифметического переупорядочения (получено в марте 2010 г.).
  20. ^ Бёрье Линд, Оптимизация производительности приложений , Sun BluePrints OnLine (март 2002 г.).
  21. ^ Эрик Флигал, « Оптимизация Microsoft Visual C++ с плавающей запятой », Технические статьи Microsoft Visual Studio (июнь 2004 г.).
  22. ^ Мартин Дж. Корден, « Согласованность результатов с плавающей запятой при использовании компилятора Intel », технический отчет Intel (18 сентября 2009 г.).
  23. ^ Макдональд, Том (1991). «C для численных вычислений». Журнал суперкомпьютеров . 5 (1): 31–48. дои : 10.1007/BF00155856 . S2CID   27876900 .
  24. ^ Технический форум BLAS , раздел 2.7 (21 августа 2001 г.), Архивировано на Wayback Machine .
  25. ^ Что нового в Python 3.12 .
  26. ^ RFC: используйте попарное суммирование для sum, cumsum и cumprod , запрос на извлечение github.com/JuliaLang/julia № 4039 (август 2013 г.).
  27. ^ Библиотека KahanSummation в Джулии.
  28. ^ Пакет HPCsharp nuget высокопроизводительных алгоритмов .

Внешние ссылки [ править ]

Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: 6df05d147aeb43553718576202f2018f__1707451080
URL1:https://arc.ask3.ru/arc/aa/6d/8f/6df05d147aeb43553718576202f2018f.html
Заголовок, (Title) документа по адресу, URL1:
Kahan summation algorithm - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)