Vai al contenuto
Calcolo Numerico

Sistemi lineari al computer: LU, pivoting, Cholesky e metodi iterativi

Fattorizzazione LU e suo costo, pivoting parziale con esempio, Cholesky, matrici sparse, metodi di Jacobi e Gauss-Seidel, raggio spettrale e residuo.

In Geometria e Algebra i sistemi lineari si risolvevano con il metodo di Gauss su matrici 3×3. Nei problemi reali le matrici hanno migliaia o milioni di righe: la simulazione di un circuito integrato, l’analisi strutturale a elementi finiti, la ricostruzione di un’immagine in una TAC. Qui contano il costo del calcolo, la stabilità rispetto agli arrotondamenti e la struttura della matrice. Vediamo i metodi diretti (fattorizzazioni LU e di Cholesky) e quelli iterativi (Jacobi e Gauss-Seidel).

La fattorizzazione LU

L’eliminazione di Gauss, registrando i moltiplicatori usati, scompone la matrice nel prodotto

    \[ A = L\,U \]

con L triangolare inferiore (con 1 sulla diagonale e i moltiplicatori sotto) e U triangolare superiore (la forma a scala). Il sistema A\mathbf x = \mathbf b diventa due sistemi triangolari, che si risolvono per sostituzione:

    \[ L\mathbf y = \mathbf b \;\;(\text{in avanti}),\qquad U\mathbf x = \mathbf y \;\;(\text{all'indietro}) \]

Il vantaggio. La fattorizzazione costa circa \tfrac23 n^3 operazioni, ma le due sostituzioni solo 2n^2. Se si devono risolvere molti sistemi con la stessa matrice e termini noti diversi (come in un simulatore che a ogni passo cambia solo le sorgenti), si fattorizza una volta sola. Con n = 1000: 6{,}7\times 10^8 operazioni per la fattorizzazione, 2\times 10^6 per ogni nuovo termine noto.

Esempio.

    \[ A = \begin{pmatrix} 2 & 1 & 1\\ 4 & 3 & 3\\ 8 & 7 & 9\end{pmatrix} = \begin{pmatrix} 1 & 0 & 0\\ 2 & 1 & 0\\ 4 & 3 & 1\end{pmatrix}\begin{pmatrix} 2 & 1 & 1\\ 0 & 1 & 1\\ 0 & 0 & 2\end{pmatrix} \]

I moltiplicatori 2 e 4 eliminano la prima colonna, il 3 la seconda.

Il pivoting

Se durante l’eliminazione compare uno zero sulla diagonale il metodo si blocca; se compare un numero molto piccolo, si divide per esso e gli errori di arrotondamento esplodono.

Esempio. Con A = \begin{pmatrix}10^{-20} & 1\\ 1 & 1\end{pmatrix} e \mathbf b = (1, 2), la soluzione vera è circa x_1 = x_2 = 1. Senza scambi, il moltiplicatore è 10^{20}, e in doppia precisione la seconda riga diventa (0, -10^{20}) perché 1 - 10^{20} si arrotonda a -10^{20}: si ottiene x_2 = 1 e poi x_1 = 0, completamente sbagliato. Scambiando le righe prima di eliminare il risultato è corretto.

Il pivoting parziale sceglie a ogni passo, come pivot, l’elemento di modulo massimo della colonna, e scambia le righe. Così i moltiplicatori hanno modulo al massimo 1 e il metodo è stabile nella pratica. La fattorizzazione diventa PA = LU, con P matrice di permutazione. È il metodo standard di tutte le librerie numeriche (LAPACK, NumPy, MATLAB).

Matrici simmetriche definite positive: Cholesky

Se A è simmetrica e definita positiva (come le matrici di rigidezza delle strutture, le matrici di covarianza, le matrici dei nodi dei circuiti fatti solo di resistori), vale

    \[ A = L\,L^T \]

con L triangolare inferiore. La fattorizzazione di Cholesky costa la metà della LU (\tfrac13 n^3), non richiede pivoting ed è sempre stabile. Se durante il calcolo compare la radice di un numero negativo, la matrice non era definita positiva: è anche il modo più rapido di verificarlo.

Matrici sparse

Nelle applicazioni le matrici grandi sono quasi tutte fatte di zeri: nella matrice dei nodi di un circuito, ogni riga ha elementi non nulli solo per i nodi collegati direttamente. Una matrice 10^6\times 10^6 piena occuperebbe 8 terabyte in doppia precisione; sparsa, con 10 elementi per riga, 80 megabyte. I metodi diretti per matrici sparse riordinano righe e colonne per limitare il riempimento (fill-in), cioè gli zeri che diventano non nulli durante l’eliminazione. È quello che fanno SPICE e i programmi a elementi finiti.

I metodi iterativi

Per matrici enormi e sparse si preferisce spesso partire da una stima \mathbf x^{(0)} e migliorarla, senza mai fattorizzare.

Jacobi. Dall’equazione i si ricava x_i usando i valori dell’iterazione precedente per le altre incognite:

    \[ x_i^{(k+1)} = \frac{1}{a_{ii}}\Big(b_i - \sum_{j \ne i}a_{ij}\,x_j^{(k)}\Big) \]

Gauss-Seidel. Come Jacobi, ma usa subito i valori già aggiornati nella stessa iterazione. Di solito converge circa due volte più in fretta e usa meno memoria.

Esempio. 4x + y = 9, x + 3y = 7 (soluzione x = 20/11 \approx 1{,}818, y = 19/11 \approx 1{,}727). Gauss-Seidel da (0, 0): x = 9/4 = 2{,}25, y = (7 - 2{,}25)/3 = 1{,}583; poi x = 1{,}854, y = 1{,}715; poi x = 1{,}821, y = 1{,}726. In tre passi, tre cifre corrette.

Quando convergono

Scrivendo l’iterazione come \mathbf x^{(k+1)} = B\,\mathbf x^{(k)} + \mathbf c, l’errore viene moltiplicato per B a ogni passo, quindi il metodo converge se e solo se il raggio spettrale di B (il massimo modulo dei suoi autovalori, lezione di Geometria e Algebra) è minore di 1. Una condizione sufficiente facile da verificare: la matrice è a diagonale dominante, |a_{ii}| \gt \sum_{j \ne i}|a_{ij}| per ogni riga. Le matrici dei nodi dei circuiti resistivi lo sono quasi sempre.

Per i problemi più grandi si usano metodi iterativi più sofisticati, come il gradiente coniugato per le matrici simmetriche definite positive, che in aritmetica esatta arriva alla soluzione in al massimo n passi e nella pratica ne richiede molti meno, soprattutto con un buon precondizionatore.

Il residuo non basta

Dopo aver calcolato \hat{\mathbf x}, il residuo \mathbf r = \mathbf b - A\hat{\mathbf x} è facile da calcolare, ma un residuo piccolo non garantisce una soluzione accurata. L’errore relativo è limitato da

    \[ \frac{\|\mathbf x - \hat{\mathbf x}\|}{\|\mathbf x\|} \le K(A)\,\frac{\|\mathbf r\|}{\|\mathbf b\|} \]

con K(A) il numero di condizionamento della lezione sugli errori. Se K(A) è grande, anche un residuo minuscolo può nascondere un errore grande.

Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
0
Would love your thoughts, please comment.x
()
x