13  Alegbra liniowa

13.1 Iloczyn skalarny (dot product)

Dla dwóch wektorów, dot oblicza ich iloczyn skalarny.

import numpy as np

# Iloczyn skalarny dwóch wektorów
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
result = np.dot(a, b)  # 1*4 + 2*5 + 3*6
print(result)  # Wynik: 32

# Alternatywny zapis za pomocą operatora @
result = a @ b
print(result)  # Wynik: 32
32
32

13.2 Mnożenie macierzowe

Dla macierzy (tablic dwuwymiarowych), dot wykonuje standardowe mnożenie macierzowe.

import numpy as np
# Mnożenie macierzowe
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
C = np.dot(A, B)
print(C)
# Wynik:
# [[19 22]
#  [43 50]]

# To samo za pomocą operatora @
C = A @ B
print(C)
[[19 22]
 [43 50]]
[[19 22]
 [43 50]]

13.3 Mnożenie macierz-wektor

Możemy również mnożyć macierz przez wektor:

import numpy as np
# Mnożenie macierz-wektor
A = np.array([[1, 2], [3, 4]])
v = np.array([5, 6])
result = np.dot(A, v)
print(result)  # Wynik: [17 39]
[17 39]

13.4 Rozwiązywanie układów równań liniowych

Funkcja numpy.linalg.solve rozwiązuje układy równań liniowych postaci Ax = b:

import numpy as np
# Rozwiązywanie układu równań liniowych
A = np.array([[3, 1], [1, 2]])
b = np.array([9, 8])
x = np.linalg.solve(A, b)
print(x)  # Wynik: [2. 3.]

# Sprawdzenie rozwiązania
np.dot(A, x)  # Powinno być równe b
[2. 3.]
array([9., 8.])

13.5 Wyznacznik macierzy

Funkcja numpy.linalg.det oblicza wyznacznik macierzy:

import numpy as np
# Obliczanie wyznacznika
A = np.array([[1, 2], [3, 4]])
det_A = np.linalg.det(A)
print(det_A)  # Wynik: -2.0
-2.0000000000000004

13.6 Wartości i wektory własne

Funkcja numpy.linalg.eig oblicza wartości i wektory własne macierzy:

import numpy as np
# Obliczanie wartości i wektorów własnych
A = np.array([[4, -2], [1, 1]])
eigenvalues, eigenvectors = np.linalg.eig(A)
print("Wartości własne:", eigenvalues)
print("Wektory własne:")
print(eigenvectors)

# Sprawdzenie: A * v = lambda * v
for i in range(len(eigenvalues)):
    lambda_i = eigenvalues[i]
    v_i = eigenvectors[:, i]
    print(f"λ_{i} = {lambda_i}")
    print("A * v =", np.dot(A, v_i))
    print("λ * v =", lambda_i * v_i)
Wartości własne: [3. 2.]
Wektory własne:
[[0.89442719 0.70710678]
 [0.4472136  0.70710678]]
λ_0 = 3.0
A * v = [2.68328157 1.34164079]
λ * v = [2.68328157 1.34164079]
λ_1 = 2.0
A * v = [1.41421356 1.41421356]
λ * v = [1.41421356 1.41421356]

13.7 Rozkład wartości osobliwych (SVD)

Rozkład SVD jest potężnym narzędziem w analizie danych:

import numpy as np
# Rozkład SVD
A = np.array([[1, 2], [3, 4], [5, 6]])
U, s, Vh = np.linalg.svd(A)
print("Macierz U:")
print(U)
print("Wartości osobliwe:", s)
print("Macierz V^H:")
print(Vh)

# Rekonstrukcja macierzy A
S = np.zeros((A.shape[0], A.shape[1]))
S[:len(s), :len(s)] = np.diag(s)
A_reconstructed = U @ S @ Vh
print("Rekonstruowana macierz A:")
print(A_reconstructed)
Macierz U:
[[-0.2298477   0.88346102  0.40824829]
 [-0.52474482  0.24078249 -0.81649658]
 [-0.81964194 -0.40189603  0.40824829]]
Wartości osobliwe: [9.52551809 0.51430058]
Macierz V^H:
[[-0.61962948 -0.78489445]
 [-0.78489445  0.61962948]]
Rekonstruowana macierz A:
[[1. 2.]
 [3. 4.]
 [5. 6.]]

13.8 Norma macierzy/wektora

NumPy oferuje różne rodzaje norm:

import numpy as np
# Różne normy
v = np.array([3, 4])
print("Norma L1:", np.linalg.norm(v, 1))  # Norma L1: 7.0
print("Norma L2 (Euklidesowa):", np.linalg.norm(v))  # Norma L2: 5.0
print("Norma maksimum:", np.linalg.norm(v, np.inf))  # Norma maksimum: 4.0

A = np.array([[1, 2], [3, 4]])
print("Norma macierzowa Frobeniusa:", np.linalg.norm(A, 'fro'))  # Norma Frobeniusa: 5.477...
Norma L1: 7.0
Norma L2 (Euklidesowa): 5.0
Norma maksimum: 4.0
Norma macierzowa Frobeniusa: 5.477225575051661

13.9 Macierz odwrotna

Funkcja numpy.linalg.inv oblicza macierz odwrotną:

import numpy as np
# Macierz odwrotna
A = np.array([[1, 2], [3, 4]])
A_inv = np.linalg.inv(A)
print("Macierz odwrotna:")
print(A_inv)

# Sprawdzenie: A * A^(-1) = I
print("A * A^(-1):")
print(np.dot(A, A_inv))  # Powinno być bliskie macierzy jednostkowej
Macierz odwrotna:
[[-2.   1. ]
 [ 1.5 -0.5]]
A * A^(-1):
[[1.0000000e+00 0.0000000e+00]
 [8.8817842e-16 1.0000000e+00]]

13.10 Funkcja numpy.inner - iloczyn wewnętrzny

Funkcja inner oblicza iloczyn wewnętrzny dwóch tablic:

import numpy as np
# Iloczyn wewnętrzny
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
result = np.inner(a, b)
print(result)  # 1*4 + 2*5 + 3*6 = 32

# Dla tablic 2D
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
result = np.inner(A, B)
print(result)
# Jest to równoważne wykonaniu iloczynu skalarnego wzdłuż ostatniego wymiaru
32
[[17 23]
 [39 53]]

13.11 Funkcja numpy.outer - iloczyn zewnętrzny

Funkcja outer oblicza iloczyn zewnętrzny dwóch wektorów:

import numpy as np
# Iloczyn zewnętrzny
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
result = np.outer(a, b)
print(result)
# Wynik:
# [[ 4  5  6]
#  [ 8 10 12]
#  [12 15 18]]
[[ 4  5  6]
 [ 8 10 12]
 [12 15 18]]

13.12 Funkcja numpy.matmul - mnożenie macierzowe

Funkcja matmul jest podobna do dot, ale ma nieco inne zachowanie dla tablic o wymiarach większych niż 2:

import numpy as np
# Porównanie dot i matmul
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])

dot_result = np.dot(a, b)
matmul_result = np.matmul(a, b)

print("Wynik dot:")
print(dot_result)
print("Wynik matmul:")
print(matmul_result)
# Dla 2D są identyczne

# Ale dla tablic 3D i wyższych mogą się różnić
Wynik dot:
[[19 22]
 [43 50]]
Wynik matmul:
[[19 22]
 [43 50]]