Решение систем линейных уравнений с разреженными матрицами
Во многих областях науки и техники, при решении различных задач, возникает необходимость расчета систем линейных уравнений, которые иногда являются сильно разреженными. Поскольку обычно такие системы уравнений имеют большой порядок, то найти их решение (в сколь-нибудь обозримые сроки) возможно только с применением вычислительной техники. Для решения подобных задач можно использовать прямые методы (Гаусса, Крамера), и приближенные (Зейделя, градиентного спуска и др.). Прямые методы дают точное решение (ошибка может быть только из-за выхода величин при преобразованиях за пределы точности типов для используемой платформы), но обычно требуют большее количество итераций. Приближенные методы обычно работают быстрее, но могут требовать особых начальных условий или какой-либо подготовки исходных данных, и решение не всегда сходится. В данной статье рассматривается реализация метода Гаусса, модифицированная для решения разреженных систем линейных уравнений. Данный метод не является «математическим», а скорее «алгоритмическим» и показывает, как путем простых приемов можно ускорить расчет в сотни раз.
Условия применения
Рассматриваемый метод подходит для любых невырожденных (имеющих одно решение) систем линейных уравнений. В дальнейшем под словом матрица будет пониматься матрица коэффициентов линейных уравнений. Единственное условие — метод имеет смысл только для сильно разреженных матриц больших размерностей N>1000, где не менее двух третей коэффициентов равны 0. Для обычных матриц метод не даст выигрыша ни в скорости, ни в экономии памяти.
Описание метода
Метод Гаусса заключается в обнулении нижней (или верхней) полуматрицы a[i,k], где строка i=2..N, столбец k=1..i-1. Используя известное правило, что решение системы не изменится, если к одному из ее уравнений добавить другое, умноженное на произвольный коэффициент, строки матрицы поочередно, начиная с первой, домножаются на специальные коэффициенты и суммируются со всеми ниже лежащими строками. Коэффициенты подбираются таким образом, чтобы каждая строка обнуляла соответствующий ей (i=k) ниже лежащий столбец (только его поддиагональную часть). Так первая строка обнуляет первый столбец A[2..N,1], вторая – второй A[3..N,2] и т.д. до предпоследней строки. Коэффициенты рассчитываются так: например для того чтобы обнулить первый столбец нужно первую строку домножить на k=-A[2,1]/A[1,1] и прибавить ко второй, затем на k=-A[3,1]/A[1,1] и прибавить к третей и т.д. После операция повторяется со второй строкой (она домножается на соответствующие коэффициенты и складывается со всеми нижележащими строками) и т.д. до предпоследней. В результате получается матрица, под главной диагональю которой остаются одни нули. Теперь в последнем уравнении остался только один коэффициент – соответственно можно легко вычислить значение последней неизвестной: X[N]=B[N]/A[N,N], где B – вектор свободных членов уравнений, который кстати, также участвует в преобразованиях при обнулении матрицы A. В предпоследнем уравнении осталось два коэффициента, и последняя неизвестная уже известна, можно вычислить предпоследнюю: X[N-1]=(B[N-1]-A[N-1,N]*X[N])/A[N-1,N-1]. Таким образом, используя обратный ход, можно вычислить все неизвестные. В процессе преобразования матрицы не должны обнуляться коэффициенты главной диагонали. Если такое происходит, то матрица не была факторизована или неправильно составлены уравнения. Или же данная система имеет не одно решение. Более подробно о методе Гаусса можно узнать здесь.
Приведенный выше алгоритм легко реализуем. Количество итераций известно заранее и зависит только от порядка матрицы. Если его запрограммировать в классическом стиле, то он легко будет справляться с небольшими матрицами N
1000. Однако матрицы иногда бывают порядка 100 000 и более. На расчет такой матрицы классическим методом Гаусса уйдут годы, поскольку количество итераций будет огромным. Если данную матрицу хранить в обычном двумерном массиве, то при использовании типа double она будет занимать в памяти 100 000 х 100 000 х 8 = 800 000 000 байт = 800 Мб что совсем не мало. А если учесть что размер матрицы возрастает пропорционально квадрату порядка матрицы, то для матрицы порядка 400 000 это будет в 16 раз больше, т.е. 12.8 Гб!
На основании выше приведенных рассуждений можно выяснить два направления оптимизации расчета:
- Более компактное хранение информации.
- Уменьшение количества итераций.
Для разреженных матриц возможно и то, и другое:
Более компактное хранение информации — разреженные матрицы имеет смысл хранить в памяти не в виде двумерного массива, а в виде некоторой структуры, в которой сохранены только ненулевые значения коэффициентов, а также их координаты в матрице. Уменьшение количества итераций — если выше упомянутую структуру организовать особым образом, чтобы из нее можно было быстро получить нужную информацию в нужном для метода Гаусса виде, то можно сократить кол-во итераций до минимального (ведь отпадает необходимость перебирать нулевые коэффициенты, которых в матрице может быть достаточно много).
Для метода Гаусса было бы удобно быстро получать строки правее главной диагонали (которые будут умножаться на коэффициенты и суммироваться с нижележащими строками для обнуления нижней полуматрицы), столбцы ниже главной диагонали (собственно то, что будет обнуляться) и элементы главной диагонали (значения с помощью которых будут обнуляться столбцы). Поэтому удобно разбить матрицу на верхнюю полуматрицу (все, что выше главной диагонали), нижнюю полуматрицу (все, что ниже) и элементы главной диагонали. При этом нужно преобразовать систему координат:
- для главной диагонали это одна координата d, которая равна соответствующему индексу элемента главной диагонали
- для верхней и нижней полуматрицы координаты d,j, где j – смещение от главной диагонали вправо или вниз (как показано на рисунке)
Диаграмма классов
Как видно из диаграммы, связь основных компонентов осуществлена через интерфейсы, поэтому легко могут быть добавлены новые реализации классов, без изменения структуры предложенного метода.
Реализация метода
Все исходные коды приведены на C#, и представляют полный набор классов, необходимых для функционирования метода. Код написан максимально просто для понимания принципа работы метода, и поэтому не претендует на максимальную производительность и не является оптимальным. О необходимых доработках сказано в заключении. Код снабжен комментариями и не нуждается в дополнительном разъяснении.
Реализация интерфейса строк и столбцов:
Реализация интерфейса матрицы:
Абстрактный класс матрицы, который содержит поля и методы, идентичные в различных реализациях классов матриц:
На данном этапе нужно решить в каком формате хранить значения ячеек матрицы. Для очень сильно разреженных матриц, когда в каждой строке/столбце матрицы ненулевых значений совсем мало, данные лучше хранить в обычных массивах, так как поиск в небольшом массиве работает быстро. Если же матрица не сильно разрежена, то лучше использовать хэш-таблицу. Ниже приведены реализации интерфейса IRow для обоих случаев:
Реализация интерфейса строки c использованием массива для хранения значений
IРеализация интерфейса строки c использованием хэш-таблицы для хранения значений:
Полная реализация класса «матрица»:
И, наконец, реализация метода Гаусса:
Заключение
В статье рассмотрен метод решения систем линейных уравнений по алгоритму Гаусса, и приведен код реализующий этот алгоритм для C#. Хотя данный код и является полным, для использования в пользовательских приложениях он нуждается в следующей доработке:
- Как минимум нужно сделать обработку исключительных ситуаций, например таких как деление на ноль или выход индекса за пределы массива
- Реализовать расчетный алгоритм в отдельном потоке команд. Если вызывать метод calculate из основного UI потока, это остановит прием сообщений окружения («повесит программу») до момента завершения расчета
- Если необходимо рассчитать несколько систем линейных уравнений, которые отличаются только правой частью (свободными членами), то естественно нет необходимости каждый раз обнулять нижнюю полуматрицу. Для этого нужно доработать класс Gauss таким образом, чтобы он, обнуляя матрицу, сохранял историю изменений. После этого для расчета неизвестных нужно будет преобразовать только вектор свободных членов, передаваемых в метод calculate в соответствии с историей. Историю можно хранить в отдельной матрице, или в нижней полуматрице основной матрицы, которая все равно не используется при обратном ходе
- Если данный алгоритм планируется реализовать на платформе, где возможна прямая работа с указателями (Delphi, C++), то лучше по возможности использовать их, это позволит в некоторых местах выполнять меньшее количество преобразований
- Для некоторых матриц необходима доработка алгоритма факторизации, для исключения попадания нулей на главную диагональ
- В реализациях интерфейса IRow, при создании экземпляров массивов и хэш-таблиц, в явном виде не задавалось свойство capacity. На практике, чтобы оптимально использовать быстродействие и экономить память, нужно подобрать компромисное значение capacity. Так с увеличением capacity увеличивается быстродействие и расход памяти, с уменьшением – наоборот. Оптимальное значение capacity зависит от структуры матрицы и не превышает максимального количества ненулевых элементов в строке или столбце матрицы
Данный алгоритм (учитывающий указанные замечания), использовался в проекте RoboCAD для расчета систем линейных уравнений метода конечных элементов в статических расчетах конструкций, а также в динамических расчетах при интегрировании систем дифференциальных уравнений колебаний, которое сводилось к многократному решению систем линейных уравнений.
Литература
- Эндрю Троелсен. С# и платформа .NET. Библиотека программиста. — СПб.: Питер, 2004.
Книга: Решение систем линейных алгебраических уравнений для больших разреженных матриц
ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ ОБРАЗОВАТЕЛЬНОЕ
УЧРЕЖДЕНИЕ ВЫСШЕГО ПРОФЕССИОНАЛЬНОГO
“ЮЖНЫЙ ФЕДЕРАЛЬНЫЙ УНИВЕРСИТЕТ”
РЕШЕНИЕ СИСТЕМ ЛИНЕЙНЫХ
АЛГЕБРАИЧЕСКИХ УРАВНЕНИЙ ДЛЯ БОЛЬШИХ РАЗРЕЖЕННЫХ МАТРИЦ
В.А.Еремеев. Решение систем линейных алгебраических уравнений для больших разреженных матриц: Учебно-методическое пособие. – г.Ростов-на-Дону, 2008 г. – 39 с.
В данном пособии в концентрированном виде содержится информация о решении систем линейных алгебраических уравнений для разреженных матриц большой размерности. Пособие состоит из четырех основных модулей:
1. задачи линейной алгебры с разреженными матрицами на примере дискретизации уравнения Пуассона;
2. векторные и матричные нормы;
3. итерационные методы;
4. методы подпространств Крылова.
Пособие предназначено для преподавания дисциплин в рамках магистерской образовательной программы “Математическое моделирование и компьютерная механика”.
Пособие также предназначено для студентов и аспирантов факультета математики, механики и компьютерных наук, специализирующихся в области математического моделирования, вычислительной математики и механики твердого деформируемого тела.
Пособие подготовлено в рамках гранта ЮФУ K-07-T-41.
1 Задачи линейной алгебры с разреженными матрицами на примере дискретизации уравнения Пуассона 4
1.1 Решение уравнения −x 00 = f (t ) . . . . . . . . . . . . . . . 4
1.2 Решение уравнения Пуассона . . . . . . . . . . . . . . . . 6
2 Векторные и матричные нормы 11
2.1 Векторные нормы . . . . . . . . . . . . . . . . . . . . . . 11
2.2 Матричные нормы . . . . . . . . . . . . . . . . . . . . . . 13
2.3 Связь векторных и матричных норм . . . . . . . . . . . 15
3 Итерационные методы 19
3.1 Определения и условия сходимости итерационных методов 19
3.2 Метод простой итерации . . . . . . . . . . . . . . . . . . . 23
3.3 Метод Якоби . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.4 Метод Гаусса-Зейделя . . . . . . . . . . . . . . . . . . . . 25
3.5 Метод последовательной верхней релаксации (SOR) . . . 26
3.6 Метод симметричной последовательной верхней релак-
сации (SSOR) . . . . . . . . . . . . . . . . . . . . . . . . . 27
4 Методы подпространств Крылова 30
4.1 Инвариантные подпространства . . . . . . . . . . . . . . 30
4.2 Степенная последовательность и подпространства Кры-
4.3 Итерационные методы подпространств Крылова . . . . . 33
Дополнительная литература 39
1 Задачи линейной алгебры с разреженными матрицами на примере дискретизации уравнения Пуассона
Основным источником задач линейной алгебры для матриц большой размерности являются задачи, получаемые в результате дискретизации краевых задач математической физики. Далее рассмотрим два примера, иллюстрирующих особенности получаемых матричных задач.
1.1 Решение уравнения −x 00 = f (t )
Рассмотрим простейшую краевую задачу
Для дискретизации этой краевой задачи воспользуемся методом конечных разностей. Введем равномерную сетку на единичном отрезке
так, чтобы t 0 = 0, tn +1 = 1. Используем обозначения
Для дискретизации второй производной воспользуемся центральной конечной разностью
,
которая аппроксимирует x 00 (ti ) c точностью O (h 2 ). Тогда исходная краевая задача сводится к системе линейных алгебраических уравнений (СЛАУ) относительно
,
.
Эту СЛАУ можно записать также в матричном виде
Обратим внимание на следующие свойства матрицы A .
• A – разреженная матрица, она трехдиагональная;
• A – симметричная и, более того, положительно определенная. Это свойство она унаследовала от свойств оператора исходной краевой задачи;
• Структура A достаточно простая, ее элементы вычисляются по простым формулам. Следовательно, для ее хранения в памяти компьютера нет необходимости использовать типы данных, предназначенные для хранения заполненных матриц.
Обсудим размерность полученной СЛАУ. Предположим, что требуемая точность аппроксимации равна ² = 10 −6 . Поскольку ² ∼ h 2 , то
−3 , т.к. h ∼ √² . Т.е. требуемый шаг сетки нужно выбрать порядка 10 мы имеем матрицу размерности 10 3 × 10 3 . На практике, размерность должна быть выше, чтобы удовлетворить большей точности.
Рис. 1: Конечно-разностная сетка. Естественная нумерация узлов.
1.2 Решение уравнения Пуассона Рассмотрим более сложную краевую задачу
где u = u (x,y ) – неизвестная функция, f (x,y ) – известная, заданные на единичном квадрате (x,y ) ∈ Ω = [0, 1]×[0, 1]>, Γ = ∂ Ω – граница Ω. Введем на Ω равномерную сетку, так что координаты узлов даются формулами xi = hi, yi = hi, i = 0. N + 1, h = 1/ (N + 1).
Для дискретизации вторых производных в операторе Лапласа ∆ воспользуемся формулами для центральной конечной разности
,
Таким образом, исходная краевая задача сводится к СЛАУ
−u i −1,j + 4u i,j − u i +1,j − u i,j −1 − u i,j +1 = h 2f i,j (1) относительно ui,j . Для того, чтобы свести ее к стандартному виду A x = b, необходимо преобразовать ui,j к выражению с одним индексом, т.е. перенумеровать каким-то образом. Выбор нумерации влияет, вообще говоря, на структуру матрицы A .
Рассмотрим случай N = 3. Соответствующая сетка показана на рис. 1, где использована естественная нумерация внутренних узлов. Полые кружки соответствуют граничным узлам, в которых известно значение функции, а сплошные – внутренним.
Если преобразовать конечно-разностное уравнение (1) в матричное уравнение A x = b, вводя вектор неизвестных по правилу u 1, 1 = x 1, u 1, 2 = x 2, u 1, 3 = x 3, u 2, 1 = x 4. u 3, 3 = x 9,
то матрица A принимает вид | ||
|