Przejdź do treści
Przewodnik po Varlig
Strona główna Varlig

Wektory i macierze

Wektor to płaska lista liczb, na przykład kierunek w przestrzeni lub zestaw ilości. Macierz to lista wierszy. Ta strona opisuje iloczyny skalarne i wektorowe, mnożenie macierzy, wyznaczniki i macierze odwrotne, układy równań oraz narzędzia numerycznej algebry liniowej: wartości własne i rozkłady QR, LU oraz SVD.

Przydaje się do zadań z algebry liniowej, geometrii, przekształceń i problemów z wieloma niewiadomymi związanymi równaniami liniowymi. Wektory i macierze są zwykłymi listami, więc obowiązuje też opis List i statystyki, w tym indeksowanie.

Wektory

calc
dot([1, 2, 3], [4, 5, 6]) daje 32
dot([3, 4], [4, -3]) daje 0
cross([1, 0, 0], [0, 1, 0]) daje [0, 0, 1]
cross([1, 2, 3], [4, 5, 6]) daje [-3, 6, -3]
v = [3, 4]
sqrt(dot(v, v)) daje 5

dot mnoży odpowiadające elementy i sumuje wyniki. Iloczyn skalarny równy zero oznacza prostopadłość, jak dla [3, 4] i [4, -3]. cross daje wektor prostopadły do obu wejściowych: kierunek x razy wektorowo kierunek y daje z. Długość wektora to pierwiastek z jego iloczynu skalarnego z samym sobą.

dot wymaga dwóch wektorów tej samej długości, a cross dwóch trójelementowych. Do dodawania i skalowania użyj zwykłej arytmetyki list: [1, 2] + [3, 4] daje [4, 6], a 2 * [3, 4] — [6, 8].

Zapisywanie i mnożenie macierzy

Zapisz macierz jako listę wierszy, każdy w osobnych nawiasach kwadratowych:

calc
shape([[1, 2, 3], [4, 5, 6]]) daje [2, 3]
transpose([[1, 2, 3], [4, 5, 6]]) daje [[1, 4], [2, 5], [3, 6]]
identity(3) daje [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
[[1, 2], [3, 4]] * [[5, 6], [7, 8]] daje [[19, 22], [43, 50]]

shape daje [rows, columns]. transpose zamienia wiersze z kolumnami. identity(n) to macierz jednostkowa n na n, która przy mnożeniu pozostawia drugą macierz bez zmian.

* między macierzami oznacza mnożenie macierzowe. Lewa macierz musi mieć tyle kolumn, ile prawa ma wierszy. Aby pomnożyć macierz przez wektor, zapisz wektor jako kolumnę, po jednym elemencie w wierszu:

calc
# Rotate the point (3, 1) by 90° anticlockwise
rotate = [[0, -1], [1, 0]]
rotate * [[3], [1]] daje [[-1], [3]]
rotate * rotate daje [[-1, 0], [0, -1]]

Dwa obroty o 90° dają pół obrotu, dlatego rotate * rotate odwraca oba znaki. Płaska lista taka jak [3, 1] nie jest przyjmowana po prawej stronie macierzy. Jeśli masz płaską listę pod nazwą, transpose([v]) zamienia ją w kolumnę.

Macierze tego samego rozmiaru można dodawać i odejmować oraz mnożyć i dzielić przez liczbę, tak jak inne listy.

Każda operacja macierzowa podlega kilku zasadom:

  • Każdy wiersz musi mieć tę samą liczbę elementów.
  • Elementy muszą być liczbami bez jednostek. Jednostki i pieniądze nie są przyjmowane.
  • Macierz może mieć najwyżej 32 wiersze i 32 kolumny.
  • Nie ma skrótu ze średnikiem. Pisz [[1, 2], [3, 4]], a nie [1, 2; 3, 4].

Wyznacznik, macierz odwrotna, ślad i rząd

calc
M = [[2, 1], [5, 3]]
det(M) daje 1
inverse(M) daje [[3, -1], [-5, 2]]
M * inverse(M) daje [[1, 0], [0, 1]]
trace(M) daje 5
inverse([[2, 1], [1, 3]]) daje [[0.6, -0.2], [-0.2, 0.4]]
det([[1, 2], [2, 4]]) daje 0
rank([[1, 2, 3], [2, 4, 6]]) daje 1

det, inverse i trace (suma przekątnej) wymagają macierzy kwadratowej. Wyznacznik zero oznacza macierz osobliwą: wiersze nie są niezależne, więc macierz nie ma odwrotności i inverse([[1, 2], [2, 4]]) daje błąd. rank liczy niezależne wiersze i przyjmuje również macierze niekwadratowe. W ostatnim wierszu drugi wiersz macierzy jest dwukrotnością pierwszego, więc rząd wynosi 1.

Te cztery funkcje używają dokładnych ułamków zamiast liczb dziesiętnych, dlatego M * inverse(M) daje dokładnie macierz jednostkową.

Rozwiązywanie układu liniowego

Załóżmy, że trzy kawy i dwie herbaty kosztują £13,50, a jedna kawa i cztery herbaty £11,50. Wpisz ilości napojów w osobnym wierszu dla każdego równania, w stałej kolejności kolumn (kawa, herbata), a sumy w odpowiadającej liście:

calc
solve_system([[3, 2], [1, 4]], [13.5, 11.5]) daje [3.1, 2.1]

Wynik ma tę samą kolejność co kolumny: kawa kosztuje £3,10, a herbata £2,10. Pomiń symbole walut, ponieważ elementy macierzy muszą być liczbami bez jednostek.

solve_system(A, b) rozwiązuje A * x = b względem x. Wymaga macierzy kwadratowej, po jednym wierszu na niewiadomą, i płaskiej listy prawych stron tej samej długości. Używa dokładnych ułamków, więc wynik nie ma błędu zaokrąglenia.

Jeśli równania nie wyznaczają jednego rozwiązania, pojawia się błąd braku jednoznacznego rozwiązania. Na przykład solve_system([[1, 2], [2, 4]], [3, 6]) nie działa, bo drugie równanie jest podwojonym pierwszym. solve_system obsługuje tylko równania liniowe. Dla x^2 = 2 użyj solve z Sumowania, równań i rachunku różniczkowego.

Wartości własne

calc
eigenvalues([[2, 1], [1, 2]]) daje [1, 3]
eigenvalues([[4, 1], [2, 3]]) daje [5, 2]
eigenvalues([[0, -1], [1, 0]]) daje [i, -i]

eigenvalues przyjmuje macierz kwadratową i zwraca płaską listę. Obrót o 90° nie zachowuje żadnego kierunku, więc ma zespolone wartości własne i i -i; zobacz Liczby zespolone. Nie polegaj na kolejności listy.

Rozkłady: QR, LU i SVD

Każdy rozkład zwraca listę macierzy. Wydziel części przez indeksy:

calc
A = [[3, 0], [4, 5]]
parts = qr(A)
q = parts[0] daje [[0.6, -0.8], [0.8, 0.6]]
r = parts[1] daje [[5, 4], [0, 3]]
q * r daje [[3, 0], [4, 5]]

Pomnożenie czynników daje z powrotem A, więc łatwo sprawdzić rozkład. Binarna arytmetyka zmiennoprzecinkowa pozostawia drobne błędy zaokrągleń, około 10⁻¹⁵, które Varlig pokazuje w listach i macierzach jako 0. Zapisane wartości zachowują wszystkie cyfry, więc oceniaj wynik wizualnie lub przez różnicę, a nie ==.

calc
lu([[2, 1], [4, 3]]) daje [[[0, 1], [1, 0]], [[1, 0], [0.5, 1]], [[4, 3], [0, -0.5]]]

lu zwraca [P, L, U], gdzie P zapisuje zamiany wierszy, a P * A jest równe L * U. Tutaj wiersze zamieniono, aby najpierw użyć większej liczby, 4.

calc
B = [[3, 1], [4, 7]]
f = svd(B)
u = f[0]
s = f[1]
vt = f[2]
u * [[s[0], 0], [0, s[1]]] * vt daje [[3, 1], [4, 7]]
svd([[3, 4]]) daje [[[1]], [5], [[0.6, 0.8]]]

svd zwraca [U, s, Vt], gdzie s jest płaską listą wartości osobliwych od największej. W zapisie podręcznikowym B jest równe U * diag(s) * Vt. Nie ma funkcji diag, więc zbuduj macierz diagonalną samodzielnie, jak w [[s[0], 0], [0, s[1]]].

Funkcja Wymagania i zwracany wynik
identity(n) Macierz jednostkowa o wymiarach n na n
shape(A) [rows, columns]
transpose(A) Zamienione wiersze i kolumny
det(A) Wyznacznik macierzy kwadratowej
inverse(A) Macierz odwrotna do macierzy kwadratowej o niezerowym wyznaczniku
trace(A) Suma elementów przekątnej macierzy kwadratowej
rank(A) Liczba liniowo niezależnych wierszy; dowolna macierz prostokątna
solve_system(A,b) Rozwiązanie A * x = b dla macierzy kwadratowej z jednym rozwiązaniem
eigenvalues(A) Wartości własne macierzy kwadratowej, możliwie zespolone, w nieokreślonej kolejności
qr(A) [Q, R], gdzie Q * R jest w przybliżeniu równe A; dowolna macierz prostokątna
lu(A) [P, L, U], gdzie P * A jest w przybliżeniu równe L * U; macierz kwadratowa
svd(A) [U, s, Vt], gdzie s jest płaską listą; dowolna macierz prostokątna

Dla macierzy niekwadratowej qr i svd zwracają zredukowane czynniki: dla macierzy 3 na 2 macierz Q również ma 3 na 2 zamiast 3 na 3.

Co jest dokładne, a co jest odrzucane

Dodawanie, odejmowanie i mnożenie macierzy, mnożenie przez liczbę, transpose, det i inverse używają dokładnych ułamków dla każdej obsługiwanej macierzy. Nic nie jest zaokrąglane na wejściu ani przed dalszym obliczeniem, dlatego iloczyn z macierzą odwrotną daje dokładnie macierz jednostkową:

calc
[[1, 2], [3, 4]] + [[10, 20], [30, 40]] daje [[11, 22], [33, 44]]
[[1, 2], [3, 4]] - [[1, 1], [1, 1]] daje [[0, 1], [2, 3]]
3 * [[1, 2], [3, 4]] daje [[3, 6], [9, 12]]
[[1, 2], [3, 4]] * [[0, 1], [1, 0]] daje [[2, 1], [4, 3]]
det([[1, 2], [3, 4]]) daje -2
inverse([[1, 2], [3, 4]]) daje [[-2, 1], [1.5, -0.5]]
inverse([[1, 2], [3, 4]]) * [[1, 2], [3, 4]] daje [[1, 0], [0, 1]]

Każdy warunek jest sprawdzany przed pokazaniem wyniku, a komunikat wskazuje niespełniony:

calc
det([[1, 2, 3], [4, 5, 6]]) daje Unsupported: Expected a square matrix
inverse([[1, 2], [2, 4]]) daje Unsupported: Singular matrix has no inverse
[[1, 2], [3, 4]] + [[1, 2, 3], [4, 5, 6]] daje Unsupported: List dimensions do not match
[[1, 2, 3], [4, 5, 6]] * [[1, 2], [3, 4]] daje Unsupported: Matrix inner dimensions do not match

Elementy macierzy są liczbami bez jednostek. Jednostka, kwota lub liczba zespolona w dowolnym elemencie zatrzymuje obliczenie zamiast przechodzić dalej, ponieważ operacje macierzowe ich nie zachowują:

calc
det([[1 kg, 2], [3, 4]]) daje Unsupported: Matrix entries must be plain numbers; units, money and complex values are not supported

Tylko lista wierszy jest interpretowana jako macierz. Płaska lista zachowuje własne znaczenie, więc [1, 2] + [3, 4] nadal daje elementowo [4, 6], a [1, 2] * [3, 4] — błąd wskazujący dot.

Zasady dokładności równań, macierzy i statystyk zebrano w Co gwarantuje silnik.

Algorytmy i limity

det, inverse, rank i solve_system używają dokładnych ułamków w granicach rozmiaru liczb kalkulatora. eigenvalues, qr, lu i svd używają binarnej arytmetyki zmiennoprzecinkowej, więc dają bliskie przybliżenia. Iteracje kończą się po 10 000 kroków.

Macierze z prawie zależnymi wierszami, jak [[1, 1], [1, 1.0000001]], są źle uwarunkowane. Drobne błędy zaokrągleń mogą się w nich silnie powiększać, więc rozkłady i wartości własne tracą dokładność. Skrajne przypadki mogą przekroczyć limity numeryczne i dać błąd.

Pełne zasady są w dokumentacji zaawansowanej matematyki.

Przykład całościowy

Trzy paragony ze straganu podają ilości jabłek, bananów i pomarańczy oraz sumy zapłaty, ale nie ceny jednostkowe:

calc
# Each row: apples, bananas, oranges
baskets = [[2, 3, 1], [1, 2, 3], [4, 1, 2]]
paid = [2.15, 2.70, 3.05]
det(baskets) daje 25
prices = solve_system(baskets, paid) daje [0.4, 0.25, 0.6]
# Check the prices against every receipt
baskets * transpose([prices]) daje [[2.15], [2.7], [3.05]]
# What would 5 apples, 2 bananas and 4 oranges cost?
dot([5, 2, 4], prices) daje 4.9

Niezerowy wyznacznik potwierdza, że paragony wystarczają do jednoznacznego ustalenia cen: jabłka 40p, banany 25p i pomarańcze 60p. Pomnożenie koszyków przez kolumnę cen odtwarza sumy, a dot wycenia nowy koszyk na £4,90.