Ortogonalizzazione di Gram-Shmidt

Le basi ortogonali ma non ortonormali possono essere rese ortonormali dividendo i vettori della base per la loro norma.

Per il teorema di ortogonalizzazione di Gram-Shmidt: dato un prodotto scalare definito positivo \(\langle , \rangle: V \times V \to \mathbb{R}\) e un insieme di vettori linearmente indipendenti: \(v_1, v_2, ..,v_n \in V\)

Allora esistono altri vettori \(w_1,w_2,..,w_n \in V\) tali che:

  • siano due a due ortogonali: \(\langle w_i,w_j \rangle = 0\) \(\forall i \ne j\)
  • generano lo stesso sottospazio: \(Span(v_1, v_2, ..,v_n) = Span(w_1,w_2,..,w_n)\)

Inoltre ogni spazio vettoriale di dimensione finita ammette almeno una base ortogonale rispetto ad un prodotto scalare definito positivo.

Corollario Questo deriva dal fatto che i vettori di una base \(\beta = \{v_n..\}\) sono linearmente indipendenti, quindi ne deriva, nei confronti di un prod.scal.def.posit., per il teorema di sopra l'insieme \(\{w_n..\}\) che è insieme ortogonale, e dato che \(Span(v_n..)=Span(w_n..)\) allora l'insieme \(\{w_n..\}\) è anche una base, oltre ad essere ortogonale.

Processo di ortogonalizzazione di Gram-Smidt

Deriva dalla dimostrazione del teorema che:

\(w_1=v_1\)

\(w_2 = v_2 - \frac{\langle v_2,w_1\rangle}{\langle w_1,w_1\rangle}w_1\)

\(...\)

\(w_n = v_n - \frac{\langle v_n,w_1\rangle}{\langle w_1,w_1\rangle}w_1 - \frac{\langle v_n,w_2\rangle}{\langle w_2,w_2\rangle}w_2 - ... - \frac{\langle v_n,w_{n-1}\rangle}{\langle w_{n-1},w_{n-1}\rangle}w_{n-1}\)

In forma compatta:

\(w_1 = v_1\)

\(w_i = v_i - \sum_{j=1}^{i-1} \frac{\langle v_i,w_j \rangle}{\langle w_j,w_j \rangle}w_j\)

con \(i \in {2,3,..,n}\)

I vettori orrenuti \(\{w_1,..,w_n\}\) formano una base di \(V\) se \(\{v_1,..,v_n\}\) è una base di \(V\) (vedi corollario teorema). Può quindi essere ortonormalizzata dividendo i vettori per la norma.

Applicazione algoritmo

Dati dei vettori di uno spazio \(V\), per determinare una base ortonormale di \(W\):

  • stabilire se i vettori (\(v_n\)) sono linearmente indipendenti
  • se lo sono avviare il processo di ortogonalizzazione, sostituendo le componenti nella formula dove \(n\) sarà il numero di vettori presi in considerazione.
  • risolvere le varie sommatorie, sapendo già che \(w_1 = v_1\)
  • i vettori ottenuti \(w_n\) costituiscono una base ortogonale di \(W\)
  • per ottenere la base ortonormale procedere con la normalizzazione dividendoli per la norma ad es avendo ottenuto \(w_1 = (1,1,0,0)\)
  • \(w_1'=\frac{w_1}{||w_1||}=\frac{(1,1,0,0)}{\sqrt{1^2 + 1^2 + 0^2 + 0^2}} = \frac{1}{\sqrt{2}}(1,1,0,0) = (\frac{1}{\sqrt{2}},\frac{1}{\sqrt{2}},0,0)\)

Determinare una matrice ortogonale avendo una colonna

Matrice ortogonale: vedi sezione "Matrice ortogonale".

  • il vettore che costituisce la colonna (o la riga) va completato con un metodo di Completamento a base (vedi sezione "Completamento a base")
  • alla la base costruita va applicato il processo di Gram-Schmidt che ci permetterà di ottenere una base ortogonale
  • normalizzare la base ortogonale
  • scrivere la matrice ponendo i vettori della base ortonormale ottenuta per colonna (o eventualmente per riga)

Funzione riassumibile - Algoritmo ortogonalizzazione

def ortogonalizzazioneGramShmidt(forma_bilineare, definizione, vettori):
    base_ortogonale = []

    if indipendenzaLineare(vettori):

        w_1 = vettori[0]
        base_ortogonale.append(w_1)

        for i in range(1, len(vettori)):
            v_n = vettori[i]
            w_n0 = base_ortogonale[i-1]

            vn_wn0 = definizione
            for j in range(len(forma_bilineare[1])):
                vn_wn0 = vn_wn0.subs(forma_bilineare[0][j], list(v_n)[j])
                vn_wn0 = vn_wn0.subs(forma_bilineare[1][j], list(w_n0)[j])

            wn0_wn0 = definizione
            for j in range(len(forma_bilineare[1])):
                wn0_wn0 = wn0_wn0.subs(forma_bilineare[0][j], list(w_n0)[j])
                wn0_wn0 = wn0_wn0.subs(forma_bilineare[1][j], list(w_n0)[j])

            w_n = v_n - (vn_wn0 / wn0_wn0) * w_n0
            base_ortogonale.append(w_n)

        base_ortonormale = []

        for wn in base_ortogonale:
            wn_norm = vettoreNormalizzato(forma_bilineare, definizione, wn, list(wn))
            base_ortonormale.append(wn_norm)

        return {
            "baseOrtogonale": base_ortogonale,
            "baseOrtonormale": base_ortonormale
        }
    else:
        return False

Esempio - Prodotto scalare euclideo

x1,x2,x3,x4,y1,y2,y3,y4 = sp.symbols("x1 x2 x3 x4 y1 y2 y3 y4")

forma_bilineare = [(x1,x2,x3,x4), (y1,y2,y3,y4)]
definizione = x1*y1 + x2*y2 + x3*y3 + x4*y4

vettori = [
    sp.Matrix([[1,1,0,0]]),
    sp.Matrix([[1,0,0,0]]),
    sp.Matrix([[0,0,0,1]])
]

result = ortogonalizzazioneGramShmidt(forma_bilineare, definizione, vettori)
pprint.pprint(result)

{'baseOrtogonale': [Matrix([[1, 1, 0, 0]]), Matrix([[1/2, -1/2, 0, 0]]), Matrix([[0, 0, 0, 1]])], 'baseOrtonormale': [Matrix([[sqrt(2)/2, sqrt(2)/2, 0, 0]]), Matrix([[sqrt(2)/2, -sqrt(2)/2, 0, 0]]), Matrix([[0, 0, 0, 1]])]}

Come da esercizio.

Esempio - Prodotto scalare vettoriale

x1,x2,x3,x4,y1,y2,y3,y4 = sp.symbols("x1 x2 x3 x4 y1 y2 y3 y4")

forma_bilineare = [(x1,x2,x3), (y1,y2,y3)]
definizione = x1*y1 + x1*y3 + x2*y2 + x3*y1 + 2*x3*y3

vettori = [
    sp.Matrix([[0,0,1]]),
    sp.Matrix([[2,0,0]]),
    sp.Matrix([[0,-1,0]])
]

result = ortogonalizzazioneGramShmidt(forma_bilineare, definizione, vettori)
pprint.pprint(result)

{'baseOrtogonale': [Matrix([[0, 0, 1]]), Matrix([[2, 0, -1]]), Matrix([[0, -1, 0]])], 'baseOrtonormale': [Matrix([[0, 0, sqrt(2)/2]]), Matrix([[sqrt(2), 0, -sqrt(2)/2]]), Matrix([[0, -1, 0]])]}

Come da esercizio.

Funzione riassumibile - Matrice ortogonale da colonna (o riga)

def ortogonalizzazioneVettoreColonna(forma_bilineare, definizione, vettore_colonna):
    base = completamentoABase([vettore_colonna.T], 3)

    lista_base = []
    for r in range(base.shape[0]):
        lista_base.append(base.row(r))

    base_ortogonale = ortogonalizzazioneGramShmidt(forma_bilineare, definizione, lista_base)["baseOrtonormale"]

    matrice_risultante = sp.zeros(base_ortogonale[0].shape[1], len(base_ortogonale))
    for i in range(base_ortogonale[0].shape[1]):
        for j in range(len(base_ortogonale)):
            matrice_risultante[i,j] = base_ortogonale[i][0, j]

    if isMatriceOrtogonale(matrice_risultante) is True:
        return matrice_risultante
    else:
        raise Exception("Il risultato non corrisponde ad una matrice ortogonale", matrice_risultante)

Esempio

x1,x2,x3,x4,y1,y2,y3,y4 = sp.symbols("x1 x2 x3 x4 y1 y2 y3 y4")

forma_bilineare = [(x1,x2,x3), (y1,y2,y3)]
definizione = x1*y1 + x2*y2 + x3*y3

vettore_colonna = sp.Matrix([[sp.Rational('3/5'),sp.Rational('4/5'),0]]).T

result = ortogonalizzazioneVettoreColonna(forma_bilineare, definizione, vettore_colonna)
pprint.pprint(result)

Matrix([ [3/5, 4/5, 0], [4/5, -3/5, 0], [ 0, 0, 1]])

Come da esercizio.

Riferimenti

  • https://www.youmath.it/lezioni/algebra-lineare/applicazioni-lineari/3974-processo-di-ortogonalizzazione-di-gram-schmidt.html