# теория · шаг 2 из 7
Погрешность: где метод Гаусса теряет цифры
Коротко
Дробное число в компьютере хранит около шестнадцати значащих цифр, и каждое действие округляет
результат. Обычно это незаметно, но метод Гаусса умеет превращать крошечное округление в неверный ответ
двумя способами. Первый - маленький ведущий элемент: система с числом 10⁻¹⁷ на диагонали без
перестановки строк даёт x = 0 вместо x = 1. Лечится выбором ведущего элемента - на диагональ ставят
наибольшее по модулю число столбца. Второй способ не лечится перестановками: у плохо обусловленной
системы ошибка растёт вместе с размером. Матрица Гильберта при n = 5 решается с ошибкой 6 · 10⁻¹³,
при n = 13 - с ошибкой 4, хотя подстановка ответа в уравнения сходится до шестнадцатого знака.
Если ты объяснишь, почему 0.1 + 0.2 не равно 0.3, как маленький ведущий элемент уничтожает ответ и почему малая невязка не гарантирует верного решения, - листай до «Теперь сам».
Как думаешь, чему равен x
0,00000000000000001 · x + 1 · y = 1
1 · x + 1 · y = 2
Коэффициент при x в первом уравнении - 10⁻¹⁷, почти ноль. Если бы там был ноль, получилось бы
y = 1 и x = 1. С таким крошечным числом ответ должен измениться на столько же: x и y остаются
практически единицами.
Прежде чем читать дальше, ответь себе: что напечатает функция gauss из прошлого урока, если дать ей
эту систему как есть? Деления на ноль тут нет.
На что это похоже
Представь весы, которые показывают вес с точностью до грамма, и на них стоит слон. Положи ему на спину монету - показания не изменятся: монета меньше одного деления шкалы. Сними слона - на весах ноль, а не монета. Сведения о монете не просто округлились, они исчезли в момент, когда её взвесили вместе со слоном.
Дробное число - такие весы: шкала у него относительная, около шестнадцати значащих цифр. Маленькое число, прибавленное к огромному, пропадает целиком.
Механизм: шестнадцать цифр и что с ними происходит
Дробное число хранится в 64 битах: знак, порядок и 53 двоичных разряда значащей части. Это около 16 десятичных цифр при любом порядке величины. Чисел, которые так представляются точно, конечное множество, и результат каждого действия округляется до ближайшего из них.
0.1 + 0.2 0.30000000000000004 0,1 не представима точно
десять раз по 0.1 0.9999999999999999 не 1.0
10¹⁶ + 1 10¹⁶ единица меньше шага шкалы
(10¹⁶ + 1) − 10¹⁶ 0.0 монета исчезла
(1 + 10⁻¹⁵) − 1 1.11 · 10⁻¹⁵ вместо 10⁻¹⁵: ошибка 11 %
Точно в двоичной записи представимы только суммы долей 1/2, 1/4, 1/8 и так далее. Одна десятая
такой конечной суммой не является - как одна треть не записывается конечной десятичной дробью, - и
хранится округлённой.
Машинный эпсилон - наименьшая относительная прибавка, которую число ещё замечает. Его можно
измерить: делить x пополам, пока 1.0 + x отличается от 1.0. Цикл делает 53 витка и останавливается
на x ≈ 1,1 · 10⁻¹⁶ - первой прибавке, которую единица уже не замечает; последняя замеченная вдвое
больше, 2,2 · 10⁻¹⁶. Эпсилоном называют то одно, то другое число - порядок у них один.
Последняя строка таблицы - главная. Сами по себе слагаемые хранятся с шестнадцатью верными цифрами. Но
при вычитании почти равных чисел старшие цифры сокращаются, и в ответе остаются только младшие - те, что
уже были округлены. Это называют потерей значащих цифр: относительная ошибка 10⁻¹⁶ превратилась в
10⁻¹.
Теперь видно, что случилось в системе из начала. Множитель для второй строки - 1 / 10⁻¹⁷ = 10¹⁷:
вторая строка: 1 − 10¹⁷ · 1 = −10¹⁷ единица исчезла рядом с 10¹⁷
правая часть: 2 − 10¹⁷ · 1 = −10¹⁷ двойка тоже
y = −10¹⁷ / −10¹⁷ = 1
x = (1 − 1 · y) / 10⁻¹⁷ = 0 / 10⁻¹⁷ = 0 верный ответ: x = 1
Вторая строка после вычитания не помнит ни единицу, ни двойку - всё, чем уравнения отличались. Маленький ведущий элемент дал огромный множитель, а огромный множитель утопил вторую строку в первой.
Правило
Перед обработкой столбца на диагональ переставляют строку с наибольшим по модулю элементом в этом
столбце - выбор ведущего элемента. Тогда все множители по модулю не больше единицы, и строки не тонут
друг в друге. Дробные числа сравнивают не на равенство, а с допуском. Малая невязка - разность Ax − b -
не доказывает, что x близко к точному решению: у плохо обусловленной системы ошибка решения может
превышать ошибку данных в миллиарды раз.
Разбор: выбор ведущего элемента
В функцию из прошлого урока добавляются поиск наибольшего элемента в столбце и обмен строк - сразу
после строки while i < n:, перед j = i + 1:
luchshaya = i
r = i + 1
while r < n:
if abs(a[r][i]) > abs(a[luchshaya][i]):
luchshaya = r
r = r + 1
if luchshaya != i:
t = a[i]
a[i] = a[luchshaya]
a[luchshaya] = t
t = b[i]
b[i] = b[luchshaya]
b[luchshaya] = t
Обмен строк - обмен двух ссылок в списке, одно действие, а не n. Та же система, оба варианта:
ведущий элемент без выбора с выбором
10⁻¹⁷ [0.0, 1.0] [1.0, 1.0]
10⁻¹⁰ [1.000000082740371, 0.9999999999] [1.0000000001, 0.9999999999]
0 деление на ноль [1.0, 1.0]
Ответ на вопрос из начала: без выбора функция печатает x = 0. Ни ошибки, ни предупреждения - просто
неверное число. Вторая строка показывает, что беда не в одном экзотическом значении: при ведущем
элементе 10⁻¹⁰ в ответе осталось семь верных цифр из шестнадцати. Чем меньше ведущий элемент, тем
больше цифр теряется.
С выбором на диагональ встаёт единица из второго уравнения, множитель равен 10⁻¹⁷, и теперь первая
строка почти не меняет вторую - как и должно быть. Цена выбора - один проход по столбцу на каждом шаге,
n² на всё: на системе из 80 уравнений 551 050 шагов вместо 541 250.
Разбор: система, которую перестановки не спасают
Матрица Гильберта: в строке i, столбце j стоит 1 / (i + j + 1), счёт с нуля.
1 1/2 1/3
1/2 1/3 1/4
1/3 1/4 1/5
Возьмём правую часть так, чтобы точным решением были все единицы, - это суммы строк матрицы, - и
решим функцией с выбором ведущего элемента. Ошибка - насколько самая плохая компонента ответа отличается от единицы. Невязка - насколько
Ax отличается от b.
n ошибка решения невязка
3 1 · 10⁻¹⁴ 2 · 10⁻¹⁶
5 6 · 10⁻¹³ 4 · 10⁻¹⁶
7 3 · 10⁻⁸ 2 · 10⁻¹⁶
9 2 · 10⁻⁵ 4 · 10⁻¹⁶
11 0,01 4 · 10⁻¹⁶
13 4,2 9 · 10⁻¹⁶
При n = 13 в ответе вместо единиц стоят числа, которые отличаются от них на несколько единиц. А невязка - шестнадцатый
знак: подставь этот неверный ответ в уравнения, и они сойдутся с машинной точностью. Проверка
подстановкой говорит «всё верно».
Дело в самой системе. Строки матрицы Гильберта почти пропорциональны друг другу, уравнения почти
повторяют одно и то же, и очень разные x дают почти одинаковые b. Округление правой части в
шестнадцатом знаке - законная ошибка данных - сдвигает решение на единицы. Такую систему называют плохо
обусловленной, а меру этого свойства - числом обусловленности: во сколько раз относительная ошибка
решения может превысить относительную ошибку данных. У матрицы Гильберта размера 13 оно около 10¹⁸ -
больше, чем шестнадцать значащих цифр способны покрыть.
Алгоритм тут ни при чём, и лучший алгоритм не поможет. Помогает другая постановка задачи или другая арифметика.
Что ломается без этого
Первое: if x == 0.3. Сравнение дробных на равенство почти всегда ошибка. Сравнивают так:
abs(x - 0.3) < 1e-9, с допуском по смыслу задачи.
Второе: деньги в дробных числах. Десять раз по 0,1 - не единица. Суммы хранят в целых копейках или в десятичном типе.
Третье: сложение большого с маленькими. Тысяча единиц, прибавленных к 10¹⁶ по одной, не меняют
его; те же единицы, сложенные сначала между собой, дают верные 10¹⁶ + 1000. Порядок суммирования
влияет на ответ: начинать лучше с маленьких.
Четвёртое: вера в невязку. «Подставил - сошлось» проверяет, что решена какая-то близкая система, а не что найдено решение этой.
Зачем это в работе
Библиотечный решатель всегда выбирает ведущий элемент, и вместе с ответом у него можно спросить оценку
числа обусловленности - numpy.linalg.cond. Если она порядка 10¹², в ответе верны четыре цифры из
шестнадцати, как бы аккуратно ни была написана программа.
Подгонка кривой под данные - классический источник плохо обусловленных систем: многочлен высокой степени по точкам даёт матрицу, родственную матрице Гильберта. Поэтому подгонку делают через другие разложения и не берут степень выше необходимой.
Машинное обучение сознательно работает с числами одинарной и половинной точности - 32 и 16 бит - и вынуждено следить за тем же: где суммы теряют малые слагаемые и где вычитание съедает цифры.
А в прошлых главах раздела погрешности не было вовсе: целые числа и остатки точны. Быстрое преобразование Фурье из раздела «Разделяй и властвуй» как раз стоит на границе: умножение длинных чисел через него идёт в дробных числах, и длину блоков выбирают так, чтобы накопленная ошибка не дошла до половины единицы.
Теперь сам
- Что напечатает
print(0.1 + 0.2 == 0.3)и почему? - К числу
10¹⁶прибавили1.0, потом вычли10¹⁶. Что получилось? А если прибавляли2.0? - В столбце под диагональю стоят числа
0.001,−5и2, на диагонали -0.01. Какую строку поставит на диагональ выбор ведущего элемента и каким будет наибольший по модулю множитель? - Решатель вернул ответ, невязка
10⁻¹⁵. Число обусловленности матрицы около10¹⁰. Сколько верных цифр можно ждать в ответе?
Ответ. 1. False. 0,1 и 0,2 в двоичной записи - бесконечные дроби, они хранятся округлёнными,
и их сумма равна 0.30000000000000004, а ближайшее к 0,3 представимое число другое.
-
0.0: шаг шкалы около10¹⁶равен двум, единица - ровно половина шага, и при таком равенстве результат округляется к соседу с чётной значащей частью - к самому10¹⁶. С2.0получится2.0: это ровно один шаг. -
Строку с
−5: по модулю она наибольшая. Множители -0.01 / (−5),0.001 / (−5)и2 / (−5); наибольший по модулю -0,4. -
Около шести: из шестнадцати цифр десять съедает обусловленность. Невязка тут ничего не говорит.
Главное
Метод Гаусса в дробных числах - это не только n³ действий, но и n³ округлений. Маленький ведущий
элемент превращает округление в катастрофу, и от этого спасает перестановка строк: множители не больше
единицы, строки не тонут друг в друге. От плохо обусловленной системы перестановка не спасает: там
ошибку усиливает сама задача, а невязка этого не показывает. Поэтому у численного ответа два вопроса -
как он получен и насколько ему можно верить, - и второй не решается проверкой подстановкой.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий