De kolommen van een reële $$N \times N$$-matrix $$A$$ kunnen geïnterpreteerd worden als vectoren in een $$N$$-dimensionale ruimte. Deze vectoren zijn niet noodzakelijk orthonormaal (d.w.z. onderling loodrecht en met lengte 1). Het Gram-Schmidt algoritme construeert uitgaande van de kolommen van $$A$$ een reeks van $$N$$ vectoren, allen lineaire combinaties van de kolommen van $$A$$ die wel orthonormaal zijn.

Noem $$\vec{x_i}$$ de vector die geassocieerd is met de $$i$$-de kolom van $$A$$. In een eerste stap zoeken we $$N$$ vectoren $$\vec{y_i}$$ die onderling loodrecht zijn, namelijk:
$$ \vec{y_0} = \vec{x_0} $$
en voor $$0 < i < N$$:
$$ \vec{y_i} = \vec{x_i} - \sum_{j=0}^{i-1} \frac{\vec{x_i}\cdot\vec{y_j}}{\vec{y_j}\cdot\vec{y_j}} \vec{y_j} $$
In deze uitdrukking staat $$\vec{a} \cdot \vec{b}$$ voor het scalair product van de vectoren $$\vec{a}$$ en $$\vec{b}$$.
In een laatste stap, normeren we de vectoren $$\vec{y_i}$$, namelijk
$$ \vec{z_i} = \frac{\vec{y_i}}{||\vec{y_i}||} $$ met $$||\vec{a}||$$ de klassieke norm van de vector $$\vec{a}$$, gedefinieerd als de vierkantswortel uit de som van de kwadraten van de componenten van $$\vec{a}$$.

Schrijf de functie orthonormaal() met als enig argument een NumPy-tabel, met $$N$$ rijen en $$N$$ kolommen, en $$N > 0$$. Het resultaat van de functie is een NumPy-tabel, eveneens bestaande uit $$N$$ rijen en $$N$$ kolommen, waarvan de kolommen de vectoren $$\vec{z_i}$$ voorstellen, verkregen via de hierboven beschreven aanpak. Je mag aannemen dat de originele tabel niet-singulier is.

Voorbeeld

a = [[1.0, 2.0, 3.0],[4.0, 5.0, 6.0], [9.0, 8.0, 4.0]]
orthonormaal(np.array(a)) = 
[[ 0.10101525  0.61796622 -0.77968819]
 [ 0.40406102  0.69066813  0.59976014]
 [ 0.90913729 -0.37562653 -0.17992804]]