Spring naar inhoud
Varlig-handleiding
Varlig-startpagina

Vectoren en matrices

Een vector is een vlakke getallenlijst, zoals een richting in de ruimte of een reeks hoeveelheden. Een matrix is een lijst van rijen. Deze pagina behandelt inwendig en uitwendig product, matrixvermenigvuldiging, determinanten en inversen, lineaire stelsels oplossen en numerieke lineaire algebra: eigenwaarden en QR-, LU- en singulierewaardendecompositie.

Gebruik dit voor lineaire algebra, meetkunde en transformaties, of problemen met meerdere onbekenden gekoppeld door lineaire vergelijkingen. Vectoren en matrices zijn gewone lijsten; alles bij Lijsten en statistiek, zoals indexeren, geldt dus ook.

Vectoren

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

dot vermenigvuldigt overeenkomstige elementen en telt de producten op. Een inwendig product nul betekent dat de vectoren loodrecht staan, zoals [3, 4] en [4, -3]. cross geeft een vector loodrecht op beide invoervectoren: de x-richting uitwendig vermenigvuldigd met de y-richting geeft de z-richting. De vectorlengte is de vierkantswortel van het inwendig product met zichzelf.

dot vereist twee even lange vectoren; cross twee vectoren van drie elementen. Gebruik gewone lijstbewerkingen om vectoren op te tellen of te schalen: [1, 2] + [3, 4] is [4, 6] en 2 * [3, 4] is [6, 8].

Matrices schrijven en vermenigvuldigen

Schrijf een matrix als lijst van rijen, elke rij tussen eigen vierkante haken:

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

shape geeft [rows, columns]. transpose wisselt rijen en kolommen. identity(n) is de n bij n eenheidsmatrix die een andere matrix bij vermenigvuldigen ongewijzigd laat.

* tussen twee matrices is matrixvermenigvuldiging. Het aantal kolommen links moet gelijk zijn aan het aantal rijen rechts. Schrijf een vector als kolom, één element per rij, om een matrix met die vector te vermenigvuldigen:

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

Tweemaal 90° draaien is een halve draai. Daarom keert rotate * rotate beide tekens om. Een vlakke lijst zoals [3, 1] wordt rechts van een matrix niet geaccepteerd. Staat een vlakke lijst in een naam, dan maakt transpose([v]) er een kolom van.

Matrices van dezelfde vorm kun je optellen en aftrekken, en met een getal vermenigvuldigen of erdoor delen, net als andere lijsten.

Voor elke matrixbewerking gelden enkele regels:

  • Elke rij moet evenveel elementen bevatten.
  • Elementen moeten gewone getallen zijn. Eenheden en geld worden niet geaccepteerd.
  • Een matrix mag maximaal 32 rijen en 32 kolommen hebben.
  • Er is geen verkorte notatie met puntkomma’s. Schrijf [[1, 2], [3, 4]], niet [1, 2; 3, 4].

Determinant, inverse, spoor en rang

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

det, inverse en trace (de som van de diagonaal) vereisen een vierkante matrix. Een determinant nul betekent dat de matrix singulier is: de rijen zijn niet onafhankelijk en er is geen inverse. inverse([[1, 2], [2, 4]]) geeft dus een fout. rank telt onafhankelijke rijen en accepteert ook niet-vierkante matrices. In de laatste regel is rij twee het dubbele van rij één, dus de rang is 1.

Deze vier gebruiken exacte breuken in plaats van decimalen. Daarom geeft M * inverse(M) exact de eenheidsmatrix.

Een lineair stelsel oplossen

Stel dat drie koffies en twee theeën £13,50 kosten, en één koffie met vier theeën £11,50. Zet per vergelijking de aantallen drankjes in een rij, met vaste kolomvolgorde (koffie, thee), en de totalen in een overeenkomstige lijst:

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

Het resultaat volgt de kolomvolgorde: koffie kost £3,10 en thee £2,10. Laat valutatekens weg, omdat matrixelementen gewone getallen moeten zijn.

solve_system(A, b) lost A * x = b op voor x. Het vereist een vierkante matrix, één rij per onbekende, en een even lange vlakke lijst rechterleden. Het gebruikt exacte breuken, dus zonder afrondingsfout.

Bepalen de vergelijkingen geen uniek resultaat, dan volgt een fout dat het stelsel geen unieke oplossing heeft. solve_system([[1, 2], [2, 4]], [3, 6]) mislukt bijvoorbeeld omdat vergelijking twee het dubbele van de eerste is. solve_system behandelt alleen lineaire vergelijkingen. Gebruik voor bijvoorbeeld x^2 = 2 de functie solve uit Sommatie, vergelijkingen en calculus.

Eigenwaarden

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

eigenvalues neemt een vierkante matrix en geeft een vlakke lijst. Een rotatie van 90° behoudt geen richting, dus haar eigenwaarden zijn het complexe paar i en -i; zie Complexe getallen. Vertrouw niet op de lijstvolgorde.

Ontbindingen: QR, LU en SVD

Elke decompositie geeft een lijst matrices. Gebruik indexen om onderdelen eruit te halen:

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

De factoren terug vermenigvuldigen geeft weer A, zodat je de decompositie snel controleert. Binaire drijvendekommagetallen laten kleine afrondingsresten rond 10⁻¹⁵ achter; Varlig toont die in lijsten en matrices als 0. Opgeslagen waarden behouden alle cijfers. Vergelijk resultaten dus visueel of via hun verschil, niet met ==.

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

lu geeft [P, L, U]. P registreert rijwisselingen en P * A is gelijk aan L * U. Hier zijn de rijen verwisseld zodat het grotere getal, 4, eerst wordt gebruikt.

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 geeft [[3, 1], [4, 7]]
svd([[3, 4]]) geeft [[[1]], [5], [[0.6, 0.8]]]

svd geeft [U, s, Vt], met s als vlakke lijst singuliere waarden, grootste eerst. In leerboeken is B gelijk aan U * diag(s) * Vt. Er is geen functie diag; bouw de diagonaalmatrix zelf, zoals [[s[0], 0], [0, s[1]]].

Functie Benodigde invoer en resultaat
identity(n) De identiteitsmatrix van n bij n
shape(A) [rows, columns]
transpose(A) Rijen en kolommen verwisseld
det(A) Determinant van een vierkante matrix
inverse(A) Inverse van een vierkante matrix met een determinant ongelijk aan nul
trace(A) Som van de hoofddiagonaal van een vierkante matrix
rank(A) Aantal onafhankelijke rijen; elke rechthoekige matrix
solve_system(A,b) Oplossing van A * x = b voor een vierkante matrix met één oplossing
eigenvalues(A) Eigenwaarden van een vierkante matrix, mogelijk complex, zonder vaste volgorde
qr(A) [Q, R] met Q * R ongeveer gelijk aan A; elke rechthoekige matrix
lu(A) [P, L, U] met P * A ongeveer gelijk aan L * U; vierkante matrix
svd(A) [U, s, Vt], waarbij s een vlakke lijst is; elke rechthoekige matrix

Bij een niet-vierkante matrix geven qr en svd gereduceerde factoren. Voor een matrix van 3 bij 2 is Q ook 3 bij 2, niet 3 bij 3.

Wat exact is en wat wordt geweigerd

Matrices optellen, aftrekken en vermenigvuldigen, vermenigvuldiging met een getal, transpose, det en inverse gebruiken exacte breuken voor elke toegestane matrix. Niets wordt afgerond bij invoer of onderweg naar een volgende berekening. Daarom levert vermenigvuldigen met de inverse exact de eenheidsmatrix:

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

Elke voorwaarde wordt gecontroleerd voordat een resultaat verschijnt; de melding benoemt welke niet klopt:

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

Matrixelementen zijn gewone getallen. Een eenheid, geldbedrag of complexe waarde stopt de berekening in plaats van te worden meegenomen, omdat matrixbewerkingen die niet zouden behouden:

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

Alleen een lijst van rijen wordt als matrix gelezen. Een vlakke lijst behoudt haar betekenis: [1, 2] + [3, 4] blijft per element [4, 6]; [1, 2] * [3, 4] blijft een fout die naar dot verwijst.

Zie Wat de rekenengine garandeert voor alle exactheidsregels bij vergelijkingen, matrices en statistiek.

Algoritmen en limieten

det, inverse, rank en solve_system gebruiken exacte breuken binnen de grenzen voor getalgrootte. eigenvalues, qr, lu en svd gebruiken binaire drijvendekommagetallen en geven nauwkeurige benaderingen. Hun iteraties stoppen na 10.000 stappen.

Matrices met bijna afhankelijke rijen, zoals [[1, 1], [1, 1.0000001]], heten slecht geconditioneerd. Kleine afrondingsfouten kunnen sterk groeien; decomposities en eigenwaarden kunnen daardoor aan nauwkeurigheid verliezen. Zeer extreme gevallen kunnen numerieke limieten overschrijden en een fout geven.

De volledige regels staan in de referentie voor geavanceerde wiskunde.

Alles combineren

Drie marktbonnen vermelden de aantallen appels, bananen en sinaasappels en het totaalbedrag, maar niet de stukprijzen:

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) geeft 25
prices = solve_system(baskets, paid) geeft [0.4, 0.25, 0.6]
# Check the prices against every receipt
baskets * transpose([prices]) geeft [[2.15], [2.7], [3.05]]
# What would 5 apples, 2 bananas and 4 oranges cost?
dot([5, 2, 4], prices) geeft 4.9

De niet-nulle determinant bevestigt dat de bonnen één unieke prijzenset bepalen: appels 40p, bananen 25p en sinaasappels 60p. Vermenigvuldigen van de mandjes met de prijzen als kolom geeft de totalen terug; dot berekent £4,90 voor een nieuw mandje.