Kurzusok
/
Julia
Julia
/
Feladatok
/
Robotforradalom
Robotforradalom

Robotforradalom

Tanulófeladat

Bevezetés

Mi is az a „lineáris algebra"?

Rengeteg technikai definíció létezik róla a [Wikipédián][linalg-wiki], a tankönyvekben és kiváló weboldalakon, például a [3Blue1Brownon][3blue1brown], de a mi céljainkra nyugodtan lehetünk kötetlenebbek.

Note

A lineáris algebra rengeteg érdekes (és nagyon hasznos) dologról szól, amit vektorokkal és mátrixokkal lehet csinálni.

A Julia (és a Python) karbantartói imádják az ilyesmit. Az Exercism vezetősége már kevésbé.

Ez a fogalom egy hatalmas témakör egyszerűbb részeire szorítkozik, de előre szólok: ez elkerülhetetlenül meglehetősen matematikai fogalom.

Mi az a vektor?

Ez attól függ, kit kérdezel, és hogyan szeretnél megjeleníteni valami meglehetősen absztrakt dolgot.

  • Juliában a Vector{T} csupán a Array{T, 1} álneve, és az eltype T bármi lehet.
  • A fizika (nagy részében) a vektor egy nyíl length és direction értékkel N-dimenziós térben, de rögzített pozíció nélkül.
  • A lineáris algebrában a fizikusok nyila rögzítve van a térben, a farka az origóban. Emiatt a nyíl szára feleslegessé válik, így a vektort egy point-ként ábrázolhatjuk N-dimenziós térben, ahol az N elem az origótól mért távolságot jelöli az N tengely mindegyike mentén.

A mostani céljainkra hagyd figyelmen kívül a stringekből vagy karakterekből álló vektorokat. Ebben a fogalomban numerikus típusokkal fogunk dolgozni: Int, Float vagy Complex.

Mi az a mátrix?

Itt is többféle választ találsz majd.

  • Számok téglalap alakú kétdimenziós tömbje (szakadozott szélek nélkül). A négyzetes mátrix gyakori speciális eset.
  • Oszlopvektorok linear combination-ja, egymás mellé rendezve.
  • Egy transformation, amely alkalmazható egy vektorra, éppúgy, ahogy egy function-t más bemeneti típusokra alkalmazunk.

A négyzetes mátrixok néhány típusa elég gyakori ahhoz, hogy külön nevet kapjon.

Átlós mátrix: Minden nem nulla elem a main diagonal-on (bal felsőtől a jobb alsóig) található.

julia> [1 0 0; 0 2 0; 0 0 3]
3×3 Matrix{Int64}:
 1  0  0
 0  2  0
 0  0  3

# a convenient shortcut
julia> diagm(1:3)
3×3 Matrix{Int64}:
 1  0  0
 0  2  0
 0  0  3

Egységmátrix: Olyan átlós mátrix, amelynek az átlójában csak egyesek állnak (olyan okokból, amelyek később világosabbá válnak majd). Gyakran I-vel rövidítik.

julia> Matrix{Float64}(I, 3, 3)
3×3 Matrix{Float64}:
 1.0  0.0  0.0
 0.0  1.0  0.0
 0.0  0.0  1.0

Felső háromszögmátrix: Nem nulla értékek az átlón és felette, nullák alatta. Az alsó háromszögmátrixot kitalálhatod magad is.

Mátrix transzponálása

Ha tényleg fel akarod cserélni a sorokat az oszlopokkal, a permutedims() függvény megteszi, és egy új mátrixot ad vissza.

Ez másolással jár, ami nagy mátrixoknál lassú és memóriaigényes.

Lineáris algebrai célokra a transpose() függvény hasznosabb, mert gyorsan egy lazy burkolót hoz létre az eredeti mátrix körül.

Még hasznosabb az adjoint() függvény, amely a komplex számok képzetes részének előjelét is megfordítja. Ez a művelet elég gyakori ahhoz, hogy egyszerűen egy aposztrófot (') tegyünk a változó neve után, így létrehozva az adjungáltat.

julia> m
2×3 Matrix{Int64}:
 1  2  3
 4  5  6

# new, full copy
julia> permutedims(m)
3×2 Matrix{Int64}:
 1  4
 2  5
 3  6

# lazy version
julia> transpose(m)
3×2 transpose(::Matrix{Int64}) with eltype Int64:
 1  4
 2  5
 3  6

julia> mc = [1+2im 2+3im; 3+2im 1+2im]
2×2 Matrix{Complex{Int64}}:
 1+2im  2+3im
 3+2im  1+2im

# lazy conjugate transpose
julia> adjoint(mc)
2×2 adjoint(::Matrix{Complex{Int64}}) with eltype Complex{Int64}:
 1-2im  3-2im
 2-3im  1-2im

# syntactic sugar for adjoint
julia> mc'
2×2 adjoint(::Matrix{Complex{Int64}}) with eltype Complex{Int64}:
 1-2im  3-2im
 2-3im  1-2im

Szorzás

A dokumentum további része a LinearAlgebra modul funkcióit használja. Egy using LinearAlgebra sor behozza a névtérbe, de ezt nem fogjuk minden példában megismételni (túl sok vizuális zaj).

Vektorok, elemenkénti szorzat

Erről a [Vektorműveletek][vector-ops] fogalomban volt szó. A műveletjelet a .* jelöli, amely páronként működik a bemeneti vektorokon, és akkora, illetve olyan típusú kimenetet ad, mint a bemenetek.

julia> [1, 2] .* [3, 4]
2-element Vector{Int64}:
 3
 8

Vektorok, skaláris szorzat

Ezt a rendkívül gyakori műveletet a tankönyvek u ⋅ v alakban írják, és „dot” szorzatnak nevezik.

A skaláris szorzat az elemenkénti szorzat összegével egyenlő.

Két vektor a szokásos * operátorral összeszorozható, de csak akkor, ha a bal oldali vektor adjoint: ezt kényelmesen u' * v alakban írjuk. Ez a későbbi, mátrixszorzásról szóló részben válik majd világosabbá.

A kiemelt (középső) pont elérhető Juliában (\cdot és tab) a dot() függvény szintaktikai cukorkájaként. Nem kell megadni az adjungáltat, mert ezt a részletet automatikusan kezeli.

julia> using LinearAlgebra

# with element-wise syntax
julia> sum([1, 2] .* [3, 4])
11

# with u' * v syntax
julia> [1, 2]' * [3, 4]
11

# with dot()
julia> dot([1, 2], [3, 4])
11

# with \cdot syntax
julia> [1, 2] ⋅ [3, 4]
11

Komplex értékű vektorok esetén a bal oldali vektornak a konjugáltnak kell lennie (a képzetes rész előjele megfordul). A dot függvény ezt automatikusan elvégzi, az u' szintaxis kifejezetten megteszi, a sum(u .* v) viszont hibára fut, ha u és v komplex.

Normák

Mennyire „nagy” egy vektor?

A norm ezt próbálja megragadni azzal, hogy a vektort egy megfelelő skalárra redukálja.

A normáknak egy egész családját definiálják, de messze a leggyakoribb a 2-es norma, amely √(v ⋅ v).

Ez a négyzetes közép típusú művelet a pitagoraszi távolság az origótól (N-dimenziós térben). Ha a vektort nyílként képzeljük el, amelynek a farka az origóban van, akkor a 2-es norma a nyíl hossza.

Bármely p-norma kiszámítható úgy, hogy a p-t adjuk meg második argumentumként. Az 1-es norma néha hasznos: egyszerűen az elemek abszolút értékeinek összege (így nagyon gyors és könnyű kiszámítani).

# defaults to the 2-norm
julia> norm([1, 2, 3])
3.7416573867739413
# the 1-norm
julia> norm([1, -2, 3], 1)
6.0

Mátrixszorzás

Néha úgy tűnik, mintha a világ minden alkalmazott matematikusa az elmúlt 80 év nagy részét azzal töltötte volna, hogy minden számítást mátrixszorzások sorozatává alakítson.

Ez olyan művelet, amiben a számítógépek nagyon jók:

  • Rendkívül ismétlődő.
  • Hatékonyan párhuzamosítható.
  • Sok speciális hardvert fejlesztettek ki a gyorsítására, köztük azt a GPU-t is, amely valószínűleg a laptopodba van építve.

A részletek meglehetősen egyszerűek, bár első ránézésre nem túl szemléletesek.

Vegyük például, hogy az A mátrix megszoroz egy v vektort, és w kimenetet ad (a lineáris algebra szokása szerint a mátrixokat nagybetűvel, a vektorokat kisbetűvel jelöljük).

Az A legfelső sorát skalárisan megszorozzuk v-vel, hogy megkapjuk a w első elemét, a második sor adja a második elemet, és így tovább lefelé.

julia> A = [1 2; 3 4]
2×2 Matrix{Int64}:
 1  2
 3  4
julia> v = [5, 6]
2-element Vector{Int64}:
 5
 6
julia> A * v
2-element Vector{Int64}:
 17  # equals [1, 2] ⋅ [5, 6]
 39  # equals [3, 4] ⋅ [5, 6]

A mátrix * mátrix szorzás ezt kiterjeszti a jobb oldali mátrix oszlopaira.

A C = A * B esetén úgy tekinthetünk rá, hogy A megszorozza B minden oszlopát, így megkapva C megfelelő oszlopát: mátrix-vektor szorzások sorozatát.

Ezzel egyenértékűen mondhatjuk, hogy az A első sorát B minden oszlopával skalárisan megszorozzuk, így megkapva a C legfelső sorát, a második sor adja a második sort, és így tovább lefelé.

julia> A
2×2 Matrix{Int64}:
 1  2
 3  4

julia> B = [5 7; 6 8]
2×2 Matrix{Int64}:
 5  7
 6  8

# left column is the same as A*v previously
julia> A * B
2×2 Matrix{Int64}:
 17  23
 39  53
 
julia> B * A
2×2 Matrix{Int64}:
 26  38
 30  44

Ahogy a fenti példa is mutatja, a mátrixszorzás nem kommutatív: nincs egyszerű kapcsolat A*B és B*A között.

A mátrixszorzást valószínűleg nehéz vizuálisan elképzelni, ha valaki most találkozik vele először, pusztán a szöveget olvasva. A YouTube-on rengeteg videó mutatja be grafikusan, ezért keress rá a „matrix multiplication” kifejezésre, és válassz egyet a neked tetsző stílusban, részletességgel és nyelven.

Dimenziók

Két vektor skaláris szorzata azon múlik, hogy egyenlő hosszúak.

Ezt kiterjesztve, mátrixszorzásnál a bal oldali mátrix oszlopainak száma meg kell egyezzen a jobb oldali mátrix sorainak számával.

A méreteket (nrows, ncols) párokként kifejezve, ahogy a size(A) kiadja, (a, b) * (b, c) -> (a, c) adódik. A „belső” dimenziók, itt b és b, alkalmasak a skaláris szorzat elvégzésére. A „külső” dimenziók, itt a és c, határozzák meg a kimenet méreteit.

Példa téglalap alakú mátrixokkal:

julia> D = reshape(1:6, 2, 3)
2×3 reshape(::UnitRange{Int64}, 2, 3) with eltype Int64:
 1  3  5
 2  4  6

julia> E = reshape(1:12, 3, 4)
3×4 reshape(::UnitRange{Int64}, 3, 4) with eltype Int64:
 1  4  7  10
 2  5  8  11
 3  6  9  12

julia> D * E
2×4 Matrix{Int64}:
 22  49   76  103
 28  64  100  136

# (3, 4) * (2, 3) not possible
julia> E * D
ERROR: DimensionMismatch: matrix A has axes (Base.OneTo(3),Base.OneTo(4)), matrix B has axes (Base.OneTo(2),Base.OneTo(3))

Vektorpárok mátrix módra való szorzásának két lehetősége van.

A szokás szerint az u' * v-t használnánk a skaláris szorzat megfelelőjeként. A dimenziók (1, 3) * (3, 1), és a Julia a (1, 1) kimenetet skalárra egyszerűsíti (ellentétben például az R-rel).

Alternatívaként használhatjuk az u * v'-t, amelynek dimenziói (3, 1) * (1, 3) -> (3, 3), hogy minden lehetséges elempárt összeszorozzunk, és a vektorokat mátrixszá bővítsük.

julia> u = [1, 2]
2-element Vector{Int64}:
 1
 2
julia> v = [3, 4]
2-element Vector{Int64}:
 3
 4
julia> u' * v
11
julia> u * v'
2×2 Matrix{Int64}:
 3  4
 6  8

Az u' * v-t néha belső szorzatnak nevezik.

Forgatások

A mátrixszorzás egy különösen gyakori fajtája a forgatómátrixokkal való szorzás.

Kétdimenzióban van egy viszonylag egyszerű mátrix, amely θ radiánnal forgatja el a vektort az óramutató járásával ellentétes irányba.

julia> rot2d(θ, vec) = [cos(θ) -sin(θ); sin(θ) cos(θ)] * vec
rot2d (generic function with 1 method)

# unit vector in the x direction
julia> i_hat = [1, 0]
2-element Vector{Int64}:
 1
 0

# rotate 45 degrees
julia> rot2d(π/4, i_hat)
2-element Vector{Float64}:
 0.7071067811865476
 0.7071067811865475

# rotate 90 degrees -> unit vector in the y direction, j_hat
julia> rot2d(π/2, i_hat)
2-element Vector{Float64}:
 6.123233995736766e-17  # zero, within numerical error
 1.0

Utasítások

Egy robotikai startupnál dolgozol, amely egy egyszerű robotot fejleszt koncepcióbizonyítékként. Az a feladatod, hogy olyan funkcionalitást biztosíts, amellyel a robot mozgása vezérelhető.

1. A robot tájolása

Hogy nyomon követhessük a robot orientációját és kiterjedését, három jelölője van, amelyek 1 egység távolságra vannak a középpontjától. A pozíció inicializálásához vegyük a három irányvektort, normalizáljuk őket, és tegyük őket egy mátrixba.

Valósítsd meg az orientrobot(vectors) függvényt, amely három vektorból álló vektort kap. Adj vissza egy 2x3 mátrixot, amelynek oszlopaiban a normalizált vektorok szerepelnek.

julia> orientrobot([[-1,1],[1,0],[-1,-1]])
2×3 Matrix{Float64}:
 -0.707107  1.0  -0.707107
  0.707107  0.0  -0.707107

2. A robot forgatása

Ezután arra van szükségünk, hogy meg tudjuk változtatni a mozgás irányát. Ehhez el kell forgatnunk a robotot, hogy abba az irányba nézzen, amerre menni szeretne.

Valósítsd meg a rotaterobot(orientation, θ) függvényt, amely a robot orientációs mátrixát és egy θ szöget kap, amellyel az óramutató járásával ellentétes irányban elforgatjuk. Add vissza az új orientációs mátrixot.

julia> orientmatrix = initialize([[-1,1],[1,0],[-1,-1]]);

julia> rotaterobot(orientmatrix, π/2)
2×3 Matrix{Float64}:
 -0.707107  6.12323e-17   0.707107
 -0.707107  1.0          -0.707107

3. Az orientáció ellenőrzése

Ahhoz, hogy a robotot egyik helyről a másikra mozgassuk, először ellenőriznünk kell, hogy megfelelő-e az orientációja, mielőtt elmozdítanánk. Az orientációs mátrix második oszlopa a robot előrefelé néző irányát adja meg.

Valósítsd meg a robotoriented(orientation, direction) függvényt, amely egy orientációs mátrixot és egy relatív pozícióvektort kap. Add vissza a true értéket, ha a robot ugyanabba az irányba néz, mint a relatív pozícióvektor (a kerekítési hibán belül).

julia> orientmatrix = initialize([[-1,1],[1,0],[-1,-1]]);

julia> robotoriented(orientmatrix, [5, 0])
true

julia> robotoriented(orientmatrix, [0, 5])
false

julia> robotoriented(orientmatrix, [-5, 0])
false

4. A robot testének koordinátái

Mivel az orientációs mátrix a robot alakját is nyomon követi, tudnunk kell, hogy ezek a pontok hol helyezkednek el az origóhoz képest, miután a robotot elmozdítottuk. Ez segít a robotnak elkerülni az ütközéseket más objektumokkal, amikor mozog.

Valósítsd meg a bodylocation(orientation, position) függvényt, amely egy orientációs mátrixot és a robot középpontjának aktuális pozícióját kapja. Add vissza az eltolt orientációs mátrixot.

julia> orientmatrix = initialize([[-1,1],[1,0],[-1,-1]]);

julia> bodylocation(orientmatrix, [5, 3])
2×3 Matrix{Float64}:
 4.29289  6.0  4.29289
 3.70711  3.0  2.29289
Szerkesztés GitHubon A hivatkozás új ablakban vagy lapon nyílik meg
Julia Exercism

Készen állsz elkezdeni a(z) Robotforradalom feladatot?

Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) Julia nyelvet 35 fogalom128 feladat segítségével, valódi emberi mentorálással, mindez ingyen.