stringtranslate.com

Algoritmo de matriz tridiagonal

En álgebra lineal numérica , el algoritmo de la matriz tridiagonal , también conocido como algoritmo de Thomas (llamado así por Llewellyn Thomas ), es una forma simplificada de eliminación gaussiana que se puede utilizar para resolver sistemas de ecuaciones tridiagonales . Un sistema tridiagonal para n incógnitas se puede escribir como

donde y .

Para tales sistemas, la solución se puede obtener en operaciones en lugar de la eliminación gaussiana requerida . Un primer barrido elimina los , y luego una sustitución hacia atrás (abreviada) produce la solución. Los ejemplos de tales matrices surgen comúnmente de la discretización de la ecuación de Poisson 1D y la interpolación spline cúbica natural .

El algoritmo de Thomas no es estable en general, pero sí lo es en varios casos especiales, como cuando la matriz es diagonalmente dominante (ya sea por filas o columnas) o simétrica positiva definida ; [1] [2] para una caracterización más precisa de la estabilidad del algoritmo de Thomas, véase el Teorema de Higham 9.12. [3] Si se requiere estabilidad en el caso general, se recomienda en su lugar la eliminación gaussiana con pivoteo parcial (GEPP). [2]

Método

El barrido hacia adelante consiste en el cálculo de nuevos coeficientes de la siguiente manera, denotando los nuevos coeficientes con primos:

y

La solución se obtiene entonces por sustitución hacia atrás:

El método anterior no modifica los vectores de coeficientes originales, pero también debe llevar un registro de los nuevos coeficientes. Si se pueden modificar los vectores de coeficientes, entonces un algoritmo con menos contabilidad sería:

Para hacer

Seguido de la sustitución hacia atrás

La implementación como una función C , que utiliza espacio de trabajo para evitar modificar sus entradas para ac, lo que permite reutilizarlas:

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 ]) { /*  resuelve Ax = d, donde A es una matriz tridiagonal que consiste en los vectores a, b, c  X = número de ecuaciones  x[] = inicialmente contiene la entrada v, y devuelve x. indexado desde [0, ..., X - 1]  a[] = subdiagonal, indexado desde [1, ..., X - 1]  b[] = diagonal principal, indexado desde [0, ..., X - 1]  c[] = superdiagonal, indexado desde [0, ..., X - 2]  scratch[] = espacio scratch de longitud X, proporcionado por el llamador, permitiendo que a, b, c sean constantes  no realizado en este ejemplo: eliminación manual y costosa de subexpresiones comunes  */ scratch [ 0 ] = c [ 0 ] / b [ 0 ]; x [ 0 ] = x [ 0 ] / b [ 0 ];                                  /* bucle de 1 a X - 1 inclusive */ 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 ]); }                                             /* bucle desde X - 2 hasta 0 inclusive */ for ( int ix = X - 2 ; ix >= 0 ; ix -- ) x [ ix ] -= scratch [ ix ] * x [ ix + 1 ]; }                  

Derivación

La derivación del algoritmo de la matriz tridiagonal es un caso especial de eliminación gaussiana .

Supongamos que las incógnitas son , y que las ecuaciones a resolver son:

Consideremos modificar la segunda ecuación ( ) con la primera ecuación de la siguiente manera:

Lo que daría:

Tenga en cuenta que se ha eliminado de la segunda ecuación. Si se utiliza una táctica similar con la segunda ecuación modificada en la tercera ecuación, se obtiene:

Esta vez se eliminó. Si se repite este procedimiento hasta la fila, la ecuación (modificada) implicará solo una incógnita, . Esto se puede resolver y luego se puede usar para resolver la ecuación, y así sucesivamente hasta que se resuelvan todas las incógnitas.

Es evidente que los coeficientes de las ecuaciones modificadas se vuelven cada vez más complicados si se los enuncia explícitamente. Si se examina el procedimiento, los coeficientes modificados (anotados con tildes) pueden definirse recursivamente:

Para acelerar aún más el proceso de solución, se puede dividir (si no hay división por riesgo cero), los coeficientes modificados más nuevos, cada uno anotado con un primo, serán:

Esto da el siguiente sistema con las mismas incógnitas y coeficientes definidos en términos de los originales anteriores:

La última ecuación implica solo una incógnita. Al resolverla, la siguiente ecuación queda reducida a una incógnita, de modo que se puede utilizar esta sustitución hacia atrás para hallar todas las incógnitas:

Variantes

En algunas situaciones, particularmente aquellas que involucran condiciones de contorno periódicas , puede ser necesario resolver una forma ligeramente perturbada del sistema tridiagonal:

En este caso, podemos utilizar la fórmula de Sherman-Morrison para evitar las operaciones adicionales de eliminación gaussiana y seguir utilizando el algoritmo de Thomas. El método requiere resolver una versión no cíclica modificada del sistema tanto para la entrada como para un vector correctivo disperso, y luego combinar las soluciones. Esto se puede hacer de manera eficiente si ambas soluciones se calculan a la vez, ya que se puede compartir la parte directa del algoritmo de matriz tridiagonal pura.

Si indicamos por:

Entonces el sistema a resolver es:

En este caso los coeficientes y son, en general, distintos de cero, por lo que su presencia no permite aplicar directamente el algoritmo de Thomas. Por tanto, podemos considerar y como sigue: Donde es un parámetro a elegir. La matriz puede reconstruirse como . La solución se obtiene entonces de la siguiente manera: [4] primero resolvemos dos sistemas de ecuaciones tridiagonales aplicando el algoritmo de Thomas:

Luego reconstruimos la solución utilizando la fórmula de Shermann-Morrison :


La implementación como una función C , que utiliza espacio de trabajo para evitar modificar sus entradas para ac, lo que permite reutilizarlas:

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 ]) { /*  resuelve Ax = v, donde A es una matriz tridiagonal cíclica que consta de los vectores a, b, c  X = número de ecuaciones  x[] = inicialmente contiene la entrada v, y devuelve x. indexado desde [0, ..., X - 1]  a[] = subdiagonal, indexada regularmente desde [1, ..., X - 1], a[0] es la esquina inferior izquierda  b[] = diagonal principal, indexada desde [0, ..., X - 1]  c[] = superdiagonal, indexada regularmente desde [0, ..., X - 2], c[X - 1] es la esquina superior derecha  cmod[], u[] = vectores de referencia, cada uno de longitud X  */                           /* esquinas inferior izquierda y superior derecha del sistema tridiagonal cíclico respectivamente */ const double alpha = a [ 0 ]; const double beta = c [ X - 1 ];             /* arbitrario, pero elegido de tal manera que se evita la división por cero */ const double gamma = - b [ 0 ];      cmod [ 0 ] = alfa / ( b [ 0 ] -gamma ) ; u [ 0 ] = gamma / ( b [ 0 ] -gamma ); x [ 0 ] / = ( b [ 0 ] -gamma ) ;                   /* bucle de 1 a X - 2 inclusive */ 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 ; }                                                      /* manejar X - 1 */ const double m = 1.0 / ( b [ X - 1 ] - alfa * beta / gamma - beta * cmod [ X - 2 ]); u [ X - 1 ] = ( alfa - a [ X - 1 ] * u [ X - 2 ]) * m ; x [ X - 1 ] = ( x [ X - 1 ] - a [ X - 1 ] * x [ X - 2 ]) * m ;                                                      /* bucle desde X - 2 hasta 0 inclusive */ for ( int ix = X - 2 ; ix >= 0 ; ix -- ) { u [ ix ] -= cmod [ ix ] * u [ ix + 1 ]; x [ ix ] -= cmod [ ix ] * x [ ix + 1 ]; }                            constante doble hecho = ( x [ 0 ] + x [ X - 1 ] * beta / gamma ) / ( 1.0 + u [ 0 ] + u [ X - 1 ] * beta / gamma );                         /* bucle de 0 a X - 1 inclusive */ for ( int ix = 0 ; ix < X ; ix ++ ) x [ ix ] -= fact * u [ ix ]; }              

Existe también otra forma de resolver la forma ligeramente perturbada del sistema tridiagonal considerado anteriormente. [5] Consideremos dos sistemas lineales auxiliares de dimensión :

Para mayor comodidad, definimos adicionalmente y . Ahora podemos encontrar las soluciones y aplicar el algoritmo de Thomas al sistema tridiagonal de dos auxiliares.

La solución puede entonces representarse en la forma:

En efecto, multiplicando cada ecuación del segundo sistema auxiliar por , sumando con la ecuación correspondiente del primer sistema auxiliar y utilizando la representación , vemos inmediatamente que se satisfacen las ecuaciones número a del sistema original; sólo queda satisfacer la ecuación número . Para ello, considere la fórmula para y y sustituya y en la primera ecuación del sistema original. Esto produce una ecuación escalar para :

Así, encontramos:

La implementación como una función C , que utiliza espacio de trabajo para evitar modificar sus entradas para ac, lo que permite reutilizarlas:

void cyclic_thomas ( const int X , double x [ restringe X ], const double a [ restringe X ], const double b [ restringe X ], const double c [ restringe X ], double cmod [ restringe X ], double v [ restringe X ]) { /* primero resuelve un sistema de longitud X - 1 para dos lados derechos, ignorando ix == 0 */ cmod [ 1 ] = c [ 1 ] / b [ 1 ]; v [ 1 ] = - a [ 1 ] / b [ 1 ]; x [ 1 ] = x [ 1 ] / b [ 1 ];                                          /* bucle de 2 a X - 1 inclusive */ 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 ; }                                                      /* manejar 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 ;                                                           /* bucle desde X - 2 hasta 1 inclusive */ 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 ]);                         /* bucle de 1 a X - 1 inclusive */ for ( int ix = 1 ; ix < X ; ix ++ ) x [ ix ] += x [ 0 ] * v [ ix ]; }              

En ambos casos los sistemas auxiliares a resolver son genuinamente tridiagonales, por lo que la complejidad computacional global de la resolución del sistema sigue siendo lineal con respecto a la dimensión del sistema , es decir, las operaciones aritméticas.

En otras situaciones, el sistema de ecuaciones puede ser tridiagonal en bloques (véase matriz de bloques ), con submatrices más pequeñas dispuestas como elementos individuales en el sistema matricial anterior (por ejemplo, el problema de Poisson 2D ). Se han desarrollado formas simplificadas de eliminación gaussiana para estas situaciones. [6]

El libro de texto Matemáticas numéricas de Alfio Quarteroni , Sacco y Saleri enumera una versión modificada del algoritmo que evita algunas de las divisiones (utilizando en su lugar multiplicaciones), lo que resulta beneficioso en algunas arquitecturas de computadoras.

Se han publicado solucionadores tridiagonales paralelos para muchas arquitecturas vectoriales y paralelas, incluidas las GPU [7] [8]

Para un tratamiento extenso de los solucionadores tridiagonales paralelos y tridiagonales en bloque, consulte [9].

Referencias

  1. ^ Pradip Niyogi (2006). Introducción a la dinámica de fluidos computacional . Pearson Education India. pág. 76. ISBN 978-81-7758-764-7.
  2. ^ ab Biswa Nath Datta (2010). Álgebra lineal numérica y aplicaciones, segunda edición . SIAM. pág. 162. ISBN 978-0-89871-765-5.
  3. ^ Nicholas J. Higham (2002). Precisión y estabilidad de algoritmos numéricos: segunda edición . SIAM. pág. 175. ISBN 978-0-89871-802-7.
  4. ^ Batista, Milán; Ibrahim Karawia, Abdel Rahman A. (2009). "El uso de la fórmula de Sherman–Morrison–Woodbury para resolver sistemas de ecuaciones lineales de bloques cíclicos tridiagonales y de bloques cíclicos pentadiagonales". Matemáticas Aplicadas y Computación . 210 (2): 558–563. doi :10.1016/j.amc.2009.01.003. ISSN  0096-3003.
  5. ^ Ryaben'kii, Victor S.; Tsynkov, Semyon V. (2 de noviembre de 2006), "Introducción", Una introducción teórica al análisis numérico , Chapman y Hall/CRC, págs. 1–19, doi :10.1201/9781420011166-1, ISBN 978-0-429-14339-7, consultado el 25 de mayo de 2022
  6. ^ Quarteroni, Alfio ; Sacco, Ricardo; Saleri, Fausto (2007). "Sección 3.8". Matemáticas Numéricas . Springer, Nueva York. ISBN 978-3-540-34658-6.
  7. ^ Chang, L.-W.; Hwu, W,-M. (2014). "Una guía para implementar solucionadores tridiagonales en GPU". En V. Kidratenko (ed.). Cálculos numéricos con GPU . Springer. ISBN 978-3-319-06548-9.{{cite conference}}: CS1 maint: multiple names: authors list (link)
  8. ^ Venetis, es decir; Kouris, A.; Sobczyk, A.; Gallopoulos, E.; Sameh, A. (2015). "Un solucionador tridiagonal directo basado en rotaciones de Givens para arquitecturas de GPU". Computación Paralela . 49 : 101-116. doi :10.1016/j.parco.2015.03.008.
  9. ^ Gallopoulos, E.; Philippe, B.; Sameh, AH (2016). "Capítulo 5". Paralelismo en cálculos matriciales . Springer. ISBN 978-94-017-7188-7.