Didattica & Progetti

Sistemi di Equazioni Lineari

← Torna ad Algebra Lineare

Sistemi Lineari: Teoria, Interpretazione Geometrica e Metodi di Risoluzione
1. Definizione e Forma Matriciale
Definizione Un sistema di equazioni lineari è un insieme di $m$ equazioni lineari in $n$ incognite: $$\begin{cases} a_{11}x_1 + a_{12}x_2 + \cdots + a_{1n}x_n = b_1 \\ a_{21}x_1 + a_{22}x_2 + \cdots + a_{2n}x_n = b_2 \\ \vdots \\ a_{m1}x_1 + a_{m2}x_2 + \cdots + a_{mn}x_n = b_m \end{cases}$$
dove:
  • $a_{ij}$ sono i coefficienti (numeri noti)
  • $x_j$ sono le incognite (da determinare)
  • $b_i$ sono i termini noti
Forma Matriciale Il sistema può essere scritto in forma compatta come: $$A\mathbf{x} = \mathbf{b}$$
dove:
  • $A = [a_{ij}]_{m \times n}$ è la matrice dei coefficienti
  • $\mathbf{x} = \begin{bmatrix} x_1 \\ x_2 \\ \vdots \\ x_n \end{bmatrix}$ è il vettore delle incognite
  • $\mathbf{b} = \begin{bmatrix} b_1 \\ b_2 \\ \vdots \\ b_m \end{bmatrix}$ è il vettore dei termini noti

La matrice completa (o aumentata) è: $[A|\mathbf{b}]$
2. Interpretazione Geometrica
Sistemi 2×2
(Due Equazioni, Due Incognite)
Ogni equazione lineare in due incognite rappresenta una retta nel piano cartesiano.

$$\begin{cases} a_1x + b_1y = c_1 \quad \text{(retta 1)} \\ a_2x + b_2y = c_2 \quad \text{(retta 2)} \end{cases}$$
Tre possibili situazioni:

Una soluzione (rette incidenti) Infinite soluzioni (rette coincidenti) Nessuna soluzione (rette parallele)

  1. Determinato: Le rette si intersecano in un punto → una soluzione unica
  2. Indeterminato: Le rette coincidono → infinite soluzioni
  3. Impossibile: Le rette sono parallele distinte → nessuna soluzione
Sistemi 3×3
(Tre Equazioni, Tre Incognite)
Ogni equazione lineare in tre incognite rappresenta un piano nello spazio tridimensionale.

$$\begin{cases} a_1x + b_1y + c_1z = d_1 \quad \text{(piano 1)} \\ a_2x + b_2y + c_2z = d_2 \quad \text{(piano 2)} \\ a_3x + b_3y + c_3z = d_3 \quad \text{(piano 3)} \end{cases}$$
Possibili situazioni:
  • Un punto: I tre piani si incontrano in un unico punto → sistema determinato
  • Una retta: I tre piani si intersecano lungo una retta → infinite soluzioni ($\infty^1$)
  • Un piano: I tre piani coincidono → infinite soluzioni ($\infty^2$)
  • Nessun punto comune: I piani non hanno intersezione → sistema impossibile

Esempio di tre piani che si incontrano in un punto:
P (soluzione)
3. Teorema di Rouché-Capelli
Enunciato Il Teorema di Rouché-Capelli stabilisce le condizioni di esistenza e unicità delle soluzioni.

Dato il sistema $A\mathbf{x} = \mathbf{b}$, sia:
  • $r_A = \text{rank}(A)$ il rango della matrice dei coefficienti
  • $r_{Ab} = \text{rank}([A|\mathbf{b}])$ il rango della matrice completa
  • $n$ il numero di incognite

Condizioni:
  1. Sistema compatibile (ammette soluzione) $\Leftrightarrow r_A = r_{Ab}$
  2. Se $r_A = r_{Ab} = n$ → sistema determinato (una soluzione)
  3. Se $r_A = r_{Ab} < n$ → sistema indeterminato ($\infty^{n-r_A}$ soluzioni)
  4. Se $r_A < r_{Ab}$ → sistema incompatibile (nessuna soluzione)
Esempio di Applicazione
$$\begin{cases} x + 2y + z = 4 \\ 2x + 4y + 2z = 8 \\ x + y + z = 3 \end{cases}$$
Matrici: $$A = \begin{bmatrix} 1 & 2 & 1 \\ 2 & 4 & 2 \\ 1 & 1 & 1 \end{bmatrix}, \quad [A|\mathbf{b}] = \left[\begin{array}{ccc|c} 1 & 2 & 1 & 4 \\ 2 & 4 & 2 & 8 \\ 1 & 1 & 1 & 3 \end{array}\right]$$
Osservazione: La seconda riga è il doppio della prima → le due righe sono linearmente dipendenti.

Riducendo a forma a scala: $$[A|\mathbf{b}] \rightarrow \left[\begin{array}{ccc|c} 1 & 2 & 1 & 4 \\ 0 & -1 & 0 & -1 \\ 0 & 0 & 0 & 0 \end{array}\right]$$
$r_A = 2$, $r_{Ab} = 2$, $n = 3$
Poiché $r_A = r_{Ab} < n$ → sistema indeterminato con $\infty^{3-2} = \infty^1$ soluzioni.
4. Metodi di Risoluzione
4.1 Metodo di Sostituzione Procedimento:
  1. Ricavare una incognita da una equazione
  2. Sostituirla nelle altre equazioni
  3. Risolvere il sistema ridotto
  4. Sostituire all'indietro per trovare tutte le incognite

Esempio: $$\begin{cases} x + y = 5 \\ 2x - y = 1 \end{cases}$$
Passo 1: Dalla prima equazione: $y = 5 - x$
Passo 2: Sostituisco nella seconda:
$2x - (5 - x) = 1$
$2x - 5 + x = 1$
$3x = 6$
$x = 2$
Passo 3: Sostituisco per trovare $y$:
$y = 5 - 2 = 3$
Soluzione: $(x, y) = (2, 3)$
4.2 Metodo di Eliminazione di Gauss Il metodo di Gauss (o eliminazione gaussiana) riduce il sistema a forma triangolare superiore mediante operazioni elementari sulle righe.

Operazioni elementari permesse:
  • Scambiare due righe
  • Moltiplicare una riga per una costante $k \neq 0$
  • Sommare ad una riga un multiplo di un'altra riga

Esempio: $$\begin{cases} 2x + y - z = 8 \\ -3x - y + 2z = -11 \\ -2x + y + 2z = -3 \end{cases}$$
Matrice aumentata: $$\begin{bmatrix} 2 & 1 & -1 & | & 8 \\ -3 & -1 & 2 & | & -11 \\ -2 & 1 & 2 & | & -3 \end{bmatrix}$$
Passo 1: $R_2 \leftarrow R_2 + \frac{3}{2}R_1$ $$\begin{bmatrix} 2 & 1 & -1 & | & 8 \\ 0 & \frac{1}{2} & \frac{1}{2} & | & 1 \\ -2 & 1 & 2 & | & -3 \end{bmatrix}$$
Passo 2: $R_3 \leftarrow R_3 + R_1$ $$\begin{bmatrix} 2 & 1 & -1 & | & 8 \\ 0 & \frac{1}{2} & \frac{1}{2} & | & 1 \\ 0 & 2 & 1 & | & 5 \end{bmatrix}$$
Passo 3: $R_3 \leftarrow R_3 - 4R_2$ $$\begin{bmatrix} 2 & 1 & -1 & | & 8 \\ 0 & \frac{1}{2} & \frac{1}{2} & | & 1 \\ 0 & 0 & -1 & | & 1 \end{bmatrix}$$

Ora il sistema è triangolare: $$\begin{cases} 2x + y - z = 8 \\ \frac{1}{2}y + \frac{1}{2}z = 1 \\ -z = 1 \end{cases}$$
Risoluzione all'indietro:
$z = -1$
$\frac{1}{2}y + \frac{1}{2}(-1) = 1 \Rightarrow y = 3$
$2x + 3 - (-1) = 8 \Rightarrow x = 2$

Soluzione: $(x, y, z) = (2, 3, -1)$
Codice Python (Gauss)
import numpy as np

def gauss_elimination(A, b):
    """
    Risolve il sistema Ax = b usando eliminazione di Gauss
    con sostituzione all'indietro
    """
    # Crea matrice aumentata
    Ab = np.column_stack([A.astype(float), b.astype(float)])
    n = len(b)

    # Fase di eliminazione (riduzione a forma triangolare)
    for i in range(n):
        # Trova il pivot massimo (pivoting parziale)
        max_row = i + np.argmax(np.abs(Ab[i:, i]))
        Ab[[i, max_row]] = Ab[[max_row, i]]

        # Elimina elementi sotto il pivot
        for j in range(i + 1, n):
            if Ab[i, i] != 0:
                factor = Ab[j, i] / Ab[i, i]
                Ab[j, i:] -= factor * Ab[i, i:]

    print("Matrice triangolare:")
    print(Ab)
    print()

    # Sostituzione all'indietro
    x = np.zeros(n)
    for i in range(n - 1, -1, -1):
        if Ab[i, i] == 0:
            raise ValueError("Sistema singolare")
        x[i] = (Ab[i, -1] - np.dot(Ab[i, i+1:n], x[i+1:n])) / Ab[i, i]

    return x

# Esempio
A = np.array([[2, 1, -1],
              [-3, -1, 2],
              [-2, 1, 2]])

b = np.array([8, -11, -3])

print("Sistema:")
print("A =")
print(A)
print("\nb =", b)
print()

soluzione = gauss_elimination(A, b)
print("Soluzione:", soluzione)

# Verifica
print("\nVerifica: A @ x =", A @ soluzione)
print("Termine noto b =", b)

# Output:
# Sistema:
# A =
# [[ 2  1 -1]
#  [-3 -1  2]
#  [-2  1  2]]
#
# b = [  8 -11  -3]
#
# Matrice triangolare:
# [[ 2.   1.  -1.   8. ]
#  [ 0.   0.5  0.5  1. ]
#  [ 0.   0.  -1.   1. ]]
#
# Soluzione: [ 2.  3. -1.]
#
# Verifica: A @ x = [  8. -11.  -3.]
# Termine noto b = [  8 -11  -3]
4.3 Metodo di Gauss-Jordan Il metodo di Gauss-Jordan estende il metodo di Gauss riducendo la matrice a forma diagonale (o forma ridotta a scala).

Differenza con Gauss:
  • Gauss: riduzione a forma triangolare superiore
  • Gauss-Jordan: riduzione a forma diagonale con pivots = 1

Il metodo elimina gli elementi sia sotto che sopra i pivots, ottenendo direttamente le soluzioni senza sostituzione all'indietro.
Codice Python (Gauss-Jordan)
import numpy as np

def gauss_jordan(A, b):
    """
    Risolve Ax = b usando il metodo di Gauss-Jordan
    Riduce a forma ridotta a scala (rref)
    """
    Ab = np.column_stack([A.astype(float), b.astype(float)])
    n = len(b)

    for i in range(n):
        # Trova pivot massimo
        max_row = i + np.argmax(np.abs(Ab[i:, i]))
        Ab[[i, max_row]] = Ab[[max_row, i]]

        # Normalizza la riga del pivot
        if Ab[i, i] != 0:
            Ab[i] = Ab[i] / Ab[i, i]

        # Elimina elementi sopra e sotto il pivot
        for j in range(n):
            if j != i and Ab[i, i] != 0:
                Ab[j] -= Ab[j, i] * Ab[i]

    print("Matrice ridotta (rref):")
    print(Ab)
    print()

    # La soluzione è nell'ultima colonna
    return Ab[:, -1]

# Esempio
A = np.array([[2, 1, -1],
              [-3, -1, 2],
              [-2, 1, 2]])

b = np.array([8, -11, -3])

print("Sistema:")
print("A =")
print(A)
print("\nb =", b)
print()

soluzione = gauss_jordan(A, b)
print("Soluzione:", soluzione)

# Verifica
print("\nVerifica: A @ x =", A @ soluzione)
print("Termine noto b =", b)

# Output:
# Sistema:
# A =
# [[ 2  1 -1]
#  [-3 -1  2]
#  [-2  1  2]]
#
# b = [  8 -11  -3]
#
# Matrice ridotta (rref):
# [[ 1.  0. -0.  2.]
#  [ 0.  1.  0.  3.]
#  [-0. -0.  1. -1.]]
#
# Soluzione: [ 2.  3. -1.]
#
# Verifica: A @ x = [  8. -11.  -3.]
# Termine noto b = [  8 -11  -3]
4.4 Metodo di Cramer Il metodo di Cramer si applica a sistemi quadrati ($n$ equazioni, $n$ incognite) con $\det(A) \neq 0$.

Formula: $$x_i = \frac{\det(A_i)}{\det(A)}$$
dove $A_i$ è la matrice ottenuta sostituendo la colonna $i$ di $A$ con il vettore $\mathbf{b}$.

Esempio sistema 2×2: $$\begin{cases} 2x + 3y = 8 \\ x - y = -1 \end{cases}$$
$$A = \begin{bmatrix} 2 & 3 \\ 1 & -1 \end{bmatrix}, \quad \mathbf{b} = \begin{bmatrix} 8 \\ -1 \end{bmatrix}$$
$\det(A) = 2(-1) - 3(1) = -2 - 3 = -5 \neq 0$ → applicabile

$$A_1 = \begin{bmatrix} 8 & 3 \\ -1 & -1 \end{bmatrix}, \quad \det(A_1) = -8 - (-3) = -5$$ $$A_2 = \begin{bmatrix} 2 & 8 \\ 1 & -1 \end{bmatrix}, \quad \det(A_2) = -2 - 8 = -10$$
$$x = \frac{-5}{-5} = 1, \quad y = \frac{-10}{-5} = 2$$
Soluzione: $(x, y) = (1, 2)$
Codice Python (Cramer)
import numpy as np

def cramer(A, b):
    """
    Risolve Ax = b usando il metodo di Cramer
    (solo per sistemi quadrati con det(A) != 0)
    """
    det_A = np.linalg.det(A)

    if abs(det_A) < 1e-10:
        raise ValueError("det(A) = 0, metodo non applicabile")

    n = len(b)
    x = np.zeros(n)

    for i in range(n):
        # Crea A_i sostituendo la colonna i con b
        A_i = A.copy()
        A_i[:, i] = b

        # Calcola x_i
        x[i] = np.linalg.det(A_i) / det_A

    return x

# Esempio
A = np.array([[2, 3],
              [1, -1]], dtype=float)

b = np.array([8, -1], dtype=float)

print("Sistema:")
print("A =")
print(A)
print("\nb =", b)
print()

print(f"det(A) = {np.linalg.det(A)}")
print()

soluzione = cramer(A, b)
print("Soluzione con Cramer:", soluzione)

# Verifica
print("\nVerifica: A @ x =", A @ soluzione)
print("Termine noto b =", b)

# Output:
# Sistema:
# A =
# [[ 2.  3.]
#  [ 1. -1.]]
#
# b = [ 8. -1.]
#
# det(A) = -5.0
#
# Soluzione con Cramer: [1. 2.]
#
# Verifica: A @ x = [ 8. -1.]
# Termine noto b = [ 8. -1.]
4.5 Metodo Matriciale (Inversa) Se $A$ è quadrata e invertibile ($\det(A) \neq 0$), la soluzione di $A\mathbf{x} = \mathbf{b}$ è: $$\mathbf{x} = A^{-1}\mathbf{b}$$
Nota: Questo metodo è teoricamente elegante ma computazionalmente inefficiente per matrici grandi. È meglio usare Gauss.
Codice Python (Inversa)
import numpy as np

# Esempio
A = np.array([[2, 1, -1],
              [-3, -1, 2],
              [-2, 1, 2]], dtype=float)

b = np.array([8, -11, -3], dtype=float)

# Metodo 1: Usando l'inversa
A_inv = np.linalg.inv(A)
x_inversa = A_inv @ b
print("Soluzione con A^(-1):", x_inversa)

# Metodo 2: Usando np.linalg.solve (più efficiente)
x_solve = np.linalg.solve(A, b)
print("Soluzione con np.linalg.solve:", x_solve)

# Verifica
print("\nVerifica: A @ x =", A @ x_solve)
print("Termine noto b =", b)

# Output:
# Soluzione con A^(-1): [ 2.  3. -1.]
# Soluzione con np.linalg.solve: [ 2.  3. -1.]
#
# Verifica: A @ x = [  8. -11.  -3.]
# Termine noto b = [  8. -11.  -3.]
Nota: np.linalg.solve() usa internamente l'eliminazione di Gauss con pivoting, che è più stabile ed efficiente del calcolo esplicito dell'inversa.
5. Confronto tra i Metodi
Efficienza Computazionale
Metodo Complessità Quando usarlo
Sostituzione $O(n^3)$ Piccoli sistemi (2-3 equazioni), calcolo manuale
Gauss $O(n^3)$ Sistemi generali, più efficiente per sistemi grandi
Gauss-Jordan $O(n^3)$ Quando serve la forma ridotta a scala
Cramer $O(n! \cdot n)$ Solo per sistemi piccoli (n ≤ 3), teoricamente elegante
Inversa $O(n^3)$ Quando serve calcolare $A^{-1}$ per altri scopi

Raccomandazione: Per sistemi grandi, usare sempre np.linalg.solve() che implementa Gauss con ottimizzazioni.
6. Esempio Completo con Visualizzazione Geometrica
Esempio 2×2 con Visualizzazione
$$\begin{cases} x + 2y = 5 \\ 2x - y = 0 \end{cases}$$
import numpy as np
import matplotlib.pyplot as plt

# Definizione del sistema
A = np.array([[1, 2],
              [2, -1]])
b = np.array([5, 0])

# Risoluzione
x_sol = np.linalg.solve(A, b)
print(f"Soluzione: x = {x_sol[0]:.2f}, y = {x_sol[1]:.2f}")

# Visualizzazione geometrica
x = np.linspace(-2, 5, 100)

# Retta 1: x + 2y = 5  =>  y = (5 - x) / 2
y1 = (5 - x) / 2

# Retta 2: 2x - y = 0  =>  y = 2x
y2 = 2 * x

# Grafico
plt.figure(figsize=(8, 6))
plt.plot(x, y1, 'b-', label='$x + 2y = 5$', linewidth=2)
plt.plot(x, y2, 'r-', label='$2x - y = 0$', linewidth=2)
plt.plot(x_sol[0], x_sol[1], 'go', markersize=10, label=f'Soluzione ({x_sol[0]:.2f}, {x_sol[1]:.2f})')
plt.axhline(y=0, color='k', linewidth=0.5)
plt.axvline(x=0, color='k', linewidth=0.5)
plt.grid(True, alpha=0.3)
plt.xlabel('x')
plt.ylabel('y')
plt.title('Intersezione di due rette')
plt.legend()
plt.axis('equal')
plt.xlim(-1, 4)
plt.ylim(-1, 4)
plt.show()

# Output:
# Soluzione: x = 1.00, y = 2.00
# [Mostra il grafico con le due rette che si intersecano in (1, 2)]
7. Applicazioni Pratiche
Circuiti Elettrici Le leggi di Kirchhoff generano sistemi lineari:
  • Legge delle correnti (KCL): somma correnti in un nodo = 0
  • Legge delle tensioni (KVL): somma tensioni in una maglia = 0

Ogni maglia genera un'equazione lineare nelle correnti incognite.
Interpolazione Polinomiale Trovare un polinomio di grado $n$ che passa per $n+1$ punti dati genera un sistema lineare nei coefficienti del polinomio.
Economia e Ricerca Operativa
  • Modelli di input-output (matrici di Leontief)
  • Problemi di bilanciamento
  • Programmazione lineare (nella risoluzione con metodo del simplesso)