В числовой линейной алгебре алгоритм трехдиагональной матрицы , также известный как алгоритм Томаса (названный в честь Ллевеллина Томаса ), является упрощенной формой метода Гаусса , который может быть использован для решения трехдиагональных систем уравнений . Трехдиагональная система для n неизвестных может быть записана как
где и .
Для таких систем решение может быть получено в операциях вместо требуемого исключения Гаусса . Первая развертка устраняет 's, а затем (сокращенная) обратная подстановка производит решение. Примеры таких матриц обычно возникают из дискретизации уравнения Пуассона 1D и естественной кубической сплайновой интерполяции .
Алгоритм Томаса не является стабильным в общем случае, но является таковым в нескольких особых случаях, например, когда матрица диагонально доминирующая (либо по строкам, либо по столбцам) или симметричная положительно определенная ; [1] [2] для более точной характеристики устойчивости алгоритма Томаса см. теорему Хайэма 9.12. [3] Если устойчивость требуется в общем случае, рекомендуется вместо этого метод исключения Гаусса с частичным поворотом (GEPP). [2]
Прямой ход состоит из вычисления новых коэффициентов следующим образом, обозначая новые коэффициенты штрихами:
и
Решение получается путем обратной подстановки:
Метод выше не изменяет исходные векторы коэффициентов, но также должен отслеживать новые коэффициенты. Если векторы коэффициентов могут быть изменены, то алгоритм с меньшим учетом выглядит так:
Для делать
с последующей обратной заменой
Реализация в виде функции C , которая использует рабочее пространство, чтобы избежать изменения своих входных данных для ac, что позволяет использовать их повторно:
void thomas ( const int X , double x [ restrict X ], const double a [ restrict X ], const double b [ restrict X ], const double c [ restrict X ], double scratch [ restrict X ]) { /* решает Ax = d, где A — трехдиагональная матрица, состоящая из векторов a, b, c X = количество уравнений x[] = изначально содержит входные данные v и возвращает x. индексируется из [0, ..., X - 1] a[] = поддиагональ, индексируется из [1, ..., X - 1] b[] = главная диагональ, индексируется из [0, ..., X - 1] c[] = наддиагональ, индексируется из [0, ..., X - 2] scratch[] = пространство для царапин длиной X, предоставленное вызывающей стороной, позволяющее сделать a, b, c константами в этом примере не выполняется: ручное дорогостоящее исключение общей подвыражения */ scratch [ 0 ] = c [ 0 ] / b [ 0 ]; х [ 0 ] = х [ 0 ] / b [ 0 ]; /* цикл от 1 до X - 1 включительно */ for ( int ix = 1 ; ix < X ; ix ++ ) { if ( ix < X -1 ){ scratch [ ix ] = c [ ix ] / ( b [ ix ] - a [ ix ] * scratch [ ix - 1 ]); } x [ ix ] = ( x [ ix ] - a [ ix ] * x [ ix - 1 ]) / ( b [ ix ] - a [ ix ] * scratch [ ix - 1 ]); } /* цикл от X - 2 до 0 включительно */ for ( int ix = X - 2 ; ix >= 0 ; ix -- ) x [ ix ] -= scratch [ ix ] * x [ ix + 1 ]; }
Вывод алгоритма трехдиагональной матрицы является частным случаем метода исключения Гаусса .
Предположим, что неизвестные равны , а уравнения, которые необходимо решить, следующие:
Рассмотрим изменение второго ( ) уравнения с помощью первого уравнения следующим образом:
что дало бы:
Обратите внимание, что было исключено из второго уравнения. Использование аналогичной тактики с измененным вторым уравнением в третьем уравнении дает:
Это время было устранено. Если эту процедуру повторять до строки; (измененное) уравнение будет включать только одно неизвестное, . Это может быть решено для и затем использовано для решения уравнения, и так далее, пока все неизвестные не будут решены для.
Очевидно, коэффициенты модифицированных уравнений становятся все более и более сложными, если их явно указать. Рассмотрев процедуру, можно сказать, что модифицированные коэффициенты (обозначенные тильдами) можно определить рекурсивно:
Чтобы еще больше ускорить процесс решения, можно разделить (если нет деления на нулевой риск), новые модифицированные коэффициенты, каждый из которых обозначен штрихом, будут иметь вид:
Это дает следующую систему с теми же неизвестными и коэффициентами, определенными через исходные, указанные выше:
Последнее уравнение содержит только одно неизвестное. Решая его, мы сводим следующее последнее уравнение к одному неизвестному, так что эта обратная подстановка может быть использована для нахождения всех неизвестных:
В некоторых ситуациях, особенно в тех, которые включают периодические граничные условия , может потребоваться решение слегка возмущенной формы трехдиагональной системы:
В этом случае мы можем использовать формулу Шермана–Моррисона, чтобы избежать дополнительных операций исключения Гаусса и по-прежнему использовать алгоритм Томаса. Метод требует решения модифицированной нециклической версии системы как для входных данных, так и для разреженного корректирующего вектора, а затем объединения решений. Это можно сделать эффективно, если оба решения вычисляются одновременно, поскольку прямая часть чистого трехдиагонального матричного алгоритма может быть общей.
Если мы укажем:
Тогда система, которую нужно решить, имеет вид:
В этом случае коэффициенты и , вообще говоря, не равны нулю, поэтому их наличие не позволяет напрямую применить алгоритм Томаса. Поэтому мы можем рассматривать и следующим образом: Где — параметр, который необходимо выбрать. Матрица может быть восстановлена как . Тогда решение получается следующим образом: [4] сначала решаем две трехдиагональные системы уравнений, применяя алгоритм Томаса:
Затем реконструируем решение, используя формулу Шермана-Моррисона :
Реализация в виде функции C , которая использует рабочее пространство, чтобы избежать изменения своих входных данных для ac, что позволяет использовать их повторно:
void cyclic_thomas ( const int X , double x [ restrict X ], const double a [ restrict X ], const double b [ restrict X ], const double c [ restrict X ], double cmod [ restrict X ] , double u [ restrict X ]) { /* решает Ax = v, где A - циклическая трехдиагональная матрица, состоящая из векторов a, b, c X = количество уравнений x[] = изначально содержит входные данные v и возвращает x. индексируется из [0, ..., X - 1] a[] = поддиагональ, регулярно индексируется из [1, ..., X - 1], a[0] - нижний левый угол b[] = главная диагональ, индексируется из [0, ..., X - 1] c[] = наддиагональ, регулярно индексируется из [0, ..., X - 2], c[X - 1] - верхний правый cmod[], u[] = векторные царапины, каждый из которых имеет длину X */ /* нижний левый и верхний правый углы циклической трехдиагональной системы соответственно */ const double alpha = a [ 0 ]; const double beta = c [ X - 1 ]; /* произвольно, но выбрано так, чтобы избежать деления на ноль */ const double gamma = - b [ 0 ]; cmod [ 0 ] = альфа / ( b [ 0 ] - гамма ); u [ 0 ] = гамма / ( b [ 0 ] - гамма ); x [ 0 ] /= ( b [ 0 ] - гамма ); /* цикл от 1 до X - 2 включительно */ for ( int ix = 1 ; ix + 1 < X ; ix ++ ) { const double m = 1.0 / ( b [ ix ] - a [ ix ] * cmod [ ix - 1 ]); cmod [ ix ] = c [ ix ] * m ; u [ ix ] = ( 0.0f - a [ ix ] * u [ ix - 1 ]) * m ; x [ ix ] = ( x [ ix ] - a [ ix ] * x [ ix - 1 ]) * m ; } /* обработка X - 1 */ const double m = 1.0 / ( b [ X - 1 ] - альфа * бета / гамма - бета * cmod [ X - 2 ]); u [ X - 1 ] = ( альфа - a [ X - 1 ] * u [ X - 2 ]) * m ; x [ X - 1 ] = ( x [ X - 1 ] - a [ X - 1 ] * x [ X - 2 ]) * m ; /* цикл от X - 2 до 0 включительно */ for ( int ix = X - 2 ; ix >= 0 ; ix -- ) { u [ ix ] -= cmod [ ix ] * u [ ix + 1 ]; x [ ix ] -= cmod [ ix ] * x [ ix + 1 ]; } const double fact = ( x [ 0 ] + x [ X - 1 ] * бета / гамма ) / ( 1,0 + u [ 0 ] + u [ X - 1 ] * бета / гамма ); /* цикл от 0 до X - 1 включительно */ for ( int ix = 0 ; ix < X ; ix ++ ) x [ ix ] -= fact * u [ ix ]; }
Существует также другой способ решения слегка возмущенной формы трехдиагональной системы, рассмотренной выше. [5] Рассмотрим две вспомогательные линейные системы размерности :
Для удобства дополнительно определим и . Теперь мы можем найти решения и , применив алгоритм Томаса к двум вспомогательным трехдиагональным системам.
Тогда решение можно представить в виде:
Действительно, умножая каждое уравнение второй вспомогательной системы на , складывая с соответствующим уравнением первой вспомогательной системы и используя представление , мы сразу видим, что уравнения с номерами по исходной системы удовлетворяются; осталось только удовлетворить уравнению с номером . Для этого рассмотрим формулы для и и подставим и в первое уравнение исходной системы. Это дает одно скалярное уравнение для :
Таким образом, мы находим:
Реализация в виде функции C , которая использует рабочее пространство, чтобы избежать изменения своих входных данных для ac, что позволяет использовать их повторно:
void cyclic_thomas ( const int X , double x [ restrict X ], const double a [ restrict X ] , const double b [ restrict X ], const double c [ restrict X ], double cmod [ restrict X ], double v [ restrict X ]) { /* сначала решим систему длины X - 1 для двух правых частей, игнорируя ix == 0 */ cmod [ 1 ] = c [ 1 ] / b [ 1 ]; v [ 1 ] = - a [ 1 ] / b [ 1 ]; x [ 1 ] = x [ 1 ] / b [ 1 ]; /* цикл от 2 до X - 1 включительно */ for ( int ix = 2 ; ix < X - 1 ; ix ++ ) { const double m = 1.0 / ( b [ ix ] - a [ ix ] * cmod [ ix - 1 ]); cmod [ ix ] = c [ ix ] * m ; v [ ix ] = ( 0.0f - a [ ix ] * v [ ix - 1 ]) * m ; x [ ix ] = ( x [ ix ] - a [ ix ] * x [ ix - 1 ]) * m ; } /* обработка X - 1 */ const double m = 1.0 / ( b [ X - 1 ] - a [ X - 1 ] * cmod [ X - 2 ]); cmod [ X - 1 ] = c [ X - 1 ] * m ; v [ X - 1 ] = ( - c [ 0 ] - a [ X - 1 ] * v [ X - 2 ]) * m ; x [ X - 1 ] = ( x [ X - 1 ] - a [ X - 1 ] * x [ X - 2 ]) * m ; /* цикл от X - 2 до 1 включительно */ for ( int ix = X - 2 ; ix >= 1 ; ix -- ) { v [ ix ] -= cmod [ ix ] * v [ ix + 1 ]; x [ ix ] -= cmod [ ix ] * x [ ix + 1 ]; } x [ 0 ] = ( x [ 0 ] - a [ 0 ] * x [ X - 1 ] - c [ 0 ] * x [ 1 ]) / ( b [ 0 ] + a [ 0 ] * v [ X - 1 ] + c [ 0 ] * v [ 1 ]); /* цикл от 1 до X - 1 включительно */ for ( int ix = 1 ; ix < X ; ix ++ ) x [ ix ] += x [ 0 ] * v [ ix ]; }
В обоих случаях вспомогательные системы, которые необходимо решить, являются действительно трехдиагональными, поэтому общая вычислительная сложность решения системы остается линейной по отношению к размерности системы , то есть арифметическим операциям.
В других ситуациях система уравнений может быть блочно-трехдиагональной (см. блочную матрицу ), с меньшими подматрицами, расположенными как отдельные элементы в вышеприведенной матричной системе (например, двумерная задача Пуассона ). Для этих ситуаций были разработаны упрощенные формы гауссовского исключения. [6]
В учебнике «Численная математика» Альфио Квартерони , Сакко и Салери приводится модифицированная версия алгоритма, которая позволяет избежать некоторых делений (используя вместо них умножения), что полезно на некоторых архитектурах компьютеров.
Параллельные трехдиагональные решатели были опубликованы для многих векторных и параллельных архитектур, включая графические процессоры [7] [8]
Для подробного рассмотрения параллельных трехдиагональных и блочных трехдиагональных решателей см. [9]
{{cite conference}}
: CS1 maint: multiple names: authors list (link)