Algoritmo di soluzione dei sistemi tridiagonali

Da Wikipedia, l'enciclopedia libera.
Vai alla navigazione Vai alla ricerca

In algebra lineare numerica, l'algoritmo di soluzione dei sistemi tridiagonali, anche conosciuto come algoritmo di Thomas (da Llewellyn Thomas), è una forma più efficiente del metodo di eliminazione di Gauss che può essere usato per la soluzione di sistemi di equazioni nella forma dove la matrice è tridiagonale.

Il sistema di equazioni si può scrivere più sinteticamente nella seguente maniera:

Dove

Questo genere di matrici compare comunemente nella discretizzazione di equazioni di Poisson 1D e nelle interpolazioni con spline.

Proprietà[modifica | modifica wikitesto]

Per questi sistemi, è possibile ottenere la soluzione in operazioni invece che nelle del metodo di eliminazione di Gauss. Un primo passaggio elimina la necessità delle e quindi una backward substitution abbreviata restituisce la soluzione.

L'algoritmo di Thomas non gode di stabilità numerica nel caso generale, ma questa può essere dimostrata per diversi casi speciali, come quando la matrice è diagonalmente dominante (per riga o per colonna) o definita positiva simmetrica[1][2]. Per una trattazione più precisa della stabilità dell'algoritmo di Thomas, vedi Higham, teorema 9.12[3]. Se, nel caso generale, è richiesta la stabilità, è preferibile l'eliminazione secondo Gauss con partial pivoting (GEPP)[2].

Algoritmo[modifica | modifica wikitesto]

Il passaggio forward consiste nel modificare i coefficienti e nella seguente maniera (i nuovi coefficienti sono denotati con l'apice):

Il secondo e ultimo passaggio è la sostituzione all'indietro (backward substitution):

L'algoritmo in linguaggio VBA è presentato qua in una versione che sovrascrive i vettori dei coefficienti.

Sub TriDiagonal_Matrix_Algorithm(N%, A#(), B#(), C#(), D#(), X#())
 Dim i%, W#
 For i = 2 To N
  W = A(i) / B(i - 1)
  B(i) = B(i) - W * C(i - 1)
  D(i) = D(i) - W * D(i - 1)
 Next i
 X(N) = D(N) / B(N)
 For i = N - 1 To 1 Step -1
  X(i) = (D(i) - C(i) * X(i + 1)) / B(i)
 Next i
End Sub

Dimostrazione[modifica | modifica wikitesto]

L'algoritmo di Thomas è un caso particolare del metodo di eliminazione di Gauss.

Consideriamo il seguente sistema di equazioni

di incognite . Consideriamo la prima e la seconda equazione e modifichiamo la seconda equazione nel modo seguente:

Otteniamo quindi:

In questo modo abbiamo eliminato il primo elemento dalla seconda equazione. Consideriamo quindi la seconda equazione modificata e la terza equazione. Modifichiamo la terza equazione nel modo seguente:

Otteniamo quindi:

In questo modo abbiamo eliminato il secondo elemento dalla terza equazione. La procedura viene ripetuta per ogni equazione: considerata l' -esima equazione questa viene modificata nel modo seguente

In questo modo l'-esima equazione ha come unica incognita . Risolta questa equazione il valore di viene utilizzato per risolvere la -esima equazione e così via fino alla prima equazione.

Chiaramente i coefficienti delle equazioni modificate si fanno via via più complicati ma, osservando la procedura, si può notare che questi possono essere definiti ricorsivamente:

dove abbiamo indicato con i coefficienti modificati.

Per ottenere l'algoritmo di Thomas modifichiamo ulteriormente i coefficienti dividendo per . In questo modo otteniamo:

Il sistema di equazioni con i coefficienti modificati diventa quindi:

Come già precedentemente detto, l'ultima equazione ha come unica incognita . Risolvendola, possiamo utilizzare il valore di e ottenere mediante sostituzione all'indietro:

Varianti[modifica | modifica wikitesto]

In alcuni casi è necessario risolvere una versione leggermente diversa di sistema tridiagonale:

In questo caso, la formula di Sherman-Morrison permette di evitare le operazioni ulteriori dell'algoritmo di Gauss e usare anche in questo caso l'algoritmo di Thomas. Il metodo richiede che si risolva una versione modificata e non ciclica del sistema, sia per l'input che per un vettore sparso di correzione, per poi combinare le soluzioni. Calcolare le due soluzioni nello stesso momento è particolarmente efficiente, dal momento che la parte forward dell'algoritmo può essere condivisa dalle due operazioni.

Se indichiamo con:

Allora il sistema da risolvere è:

In questo caso i coefficienti e sono, in generale, diversi da zero di conseguenza la loro presenza non permette di applicare l'algoritmo di Thomas direttamente.

Definiamo quindi e nel modo seguente:

Dove è un parametro da scegliere. La matrice può essere riscritta . La soluzione del sistema viene quindi trovata con la seguente procedura:[4] prima si risolvono due sistemi tridiagonali applicando l'algoritmo di Thomas:

Successivamente la soluzione viene ricostruita utilizzando la formula di Shermann-Morisson:

L'implementazione in Dev-C++ è presentata qua in una versione che sovrascrive i vettori dei coefficienti.

typedef struct{
	double A[n+2]; 
	double B[n+2];
	double C[n+2];
	double D[n+2];
} COEFFICIENTS;

//Apply Thomas Alg., unknowns x[1],...,x[n]
void ThomasAlg(double x[n+1],COEFFICIENTS* coeff){
	double u[n+1]={},v[n+1]={};
	double y[n+1]={},q[n+1]={};
	double* A=coeff->A, *B=coeff->B,*C=coeff->C,*D=coeff->D;
	double Value=0;
	double w;
	int i;
	
	u[1]=gamma;
	u[n]=C[n];
	
	v[1]=1;
	v[n]=A[1]/gamma;
	
	//create matrix B
	A[1]=0;
	B[1]=B[1]-gamma;
	B[n]=B[n]-(C[n]*A[n])/gamma;
	C[n]=0;
	
	for(i=2;i<n+1;i++){
		w=A[i]/B[i-1];	
		B[i]=B[i]-w*C[i-1];
		D[i]=D[i]-w*D[i-1];
		u[i]=u[i]-w*u[i-1];
	}
	y[n]=D[n]/B[n];
	q[n]=u[n]/B[n];
	
	for(i=n-1;i>0;i--){
		y[i]=(D[i]-C[i]*y[i+1])/B[i];
		q[i]=(u[i]-C[i]*q[i+1])/B[i];
	}
	Value=(v[1]*y[1]+v[n]*y[n])/(1+v[1]*q[1]+v[n]*q[n]);
	
	for(i=1;i<n+1;i++){
		x[i]=y[i]-q[i]*Value;
	}
}

C'è anche un altro modo per risolvere il sistema quasi tridiagonale considerato.[5] Consideriamo due sistemi lineari ausiliari di dimensioni :

Per comodità definiamo e . Applicando l'algoritmo di Thomas ai due sistemi ausiliari troviamo le soluzioni e ; la soluzione viene quindi rappresentata nella forma:

Infatti, moltiplicando ogni equazione del secondo sistema per , sommandola con la corrispondente equazione del primo sistema e usando la rappresentazione vediamo subito che le equazioni del sistema originale sono soddisfatte per ogni . Per quanto riguarda la prima equazione, consideriamo e e sostituiamo le seguenti espressioni nella prima equazione. Otteniamo quindi un'equazione che ha come unica incognita :

Risolvendola, otteniamo:

L'implementazione in Dev-C++ è presentata qua in una versione che sovrascrive i vettori dei coefficienti.

typedef struct{
	double A[n+2]; 
	double B[n+2];
	double C[n+2];
	double D[n+2];
} COEFFICIENTS;

//Apply Thomas Alg., unknowns x[1],...,x[n]
void ThomasAlg(double x[n+1],COEFFICIENTS* coeff){
	double u[n+1]={},v[n+1]={};
	double* A=coeff->A, *B=coeff->B,*C=coeff->C,*D=coeff->D;
	double w,F[n+1]={};
	F[2]=-A[2];
	F[n]=-C[n];
	int i;
	u[1]=0;
	v[1]=1;
	for(i=3;i<n+1;i++){
		w=A[i]/B[i-1];	
		B[i]=B[i]-w*C[i-1];
		D[i]=D[i]-w*D[i-1];
		F[i]=F[i]-w*F[i-1];
	}
	u[n]=D[n]/B[n];
	v[n]=F[n]/B[n];
	for(i=n-1;i>1;i--){
		u[i]=(D[i]-C[i]*u[i+1])/B[i];
		v[i]=(F[i]-C[i]*v[i+1])/B[i];
	}
	x[1]=(D[1]-A[1]*u[n]-C[1]*u[2])/(B[1]+A[1]*v[n]+C[1]*v[2]);
	
	for(i=2;i<n+1;i++){
		x[i]=u[i]+x[1]*v[i];
	}
}

In entrambi i casi i sistemi ausiliari da risolvere sono tridiagonali di conseguenza la soluzione si ottiene ancora in operazioni.

In altri casi, il sistema di equazioni potrebbe essere tridiagonale a blocchi (vedi matrice a blocchi), con sottomatrici più piccole disposte come elementi individuali del sistema matriciale descritto sopra (per esempio nelle equazioni di Poisson 2D). Sono stati sviluppati algoritmi efficienti per condurre l'eliminazione di Gauss anche per questi casi.

Il libro di testo Matematica Numerica di Quarteroni, Sacco e Saleri mostra una versione dell'algoritmo che sostituisce alcune delle divisioni con delle moltiplicazioni, in modo da migliorare l'efficienza dell'algoritmo in alcune architetture di calcolo.

Sono stati pubblicati solutori di sistemi tridiagonali paralleli per diverse architetture, incluse GPU[6][7].

Per una trattazione estensiva degli algoritmi di soluzione paralleli di sistemi tridiagonali e tridiagonali a blocchi, vedi Gallopoulos et al.[8].

Note[modifica | modifica wikitesto]

  1. ^ Pradip Niyogi, Introduction to Computational Fluid Dynamics, Pearson Education India, 2006, p. 76, ISBN 978-81-7758-764-7.
  2. ^ a b Biswa Nath Datta, Numerical Linear Algebra and Applications, Second Edition, SIAM, 2010, p. 162, ISBN 978-0-89871-765-5.
  3. ^ Nicholas J. Higham, Accuracy and Stability of Numerical Algorithms: Second Edition, SIAM, 2002, p. 175, ISBN 978-0-89871-802-7.
  4. ^ Milan Batista e Abdel Rahman A. Ibrahim Karawia, The use of the Sherman–Morrison–Woodbury formula to solve cyclic block tri-diagonal and cyclic block penta-diagonal linear systems of equations, in Applied Mathematics and Computation, vol. 210, n. 2, 2009, pp. 558–563, DOI:10.1016/j.amc.2009.01.003, ISSN 0096-3003 (WC · ACNP).
  5. ^ Victor S. Ryaben’kii e Semyon V. Tsynkov, Introduction, in A Theoretical Introduction to Numerical Analysis, Chapman and Hall/CRC, 2 novembre 2006, pp. 1–19, ISBN 978-0-429-14339-7. URL consultato il 25 maggio 2022.
  6. ^ L.-W. Chang e W,-M. Hwu, A guide for implementing tridiagonal solvers on GPUs, a cura di V. Kidratenko, Springer, 2014, ISBN 978-3-319-06548-9.
  7. ^ I.E. Venetis, A. Kouris, A. Sobczyk, E. Gallopoulos e A. Sameh, A direct tridiagonal solver based on Givens rotations for GPU architectures, in Parallel Computing, vol. 49, 2015, pp. 101-116.
  8. ^ E. Gallopoulos, B. Philippe e A.H. Sameh, Chapter 5, in Parallelism in Matrix Computations, Springer, 2016, ISBN 978-94-017-7188-7.
  Portale Matematica: accedi alle voci di Wikipedia che trattano di matematica