Práca s maticami a vektormi II
Spôsob práce s vektormi a maticami v MATLAB je natoľko zásadný tému, že je rozdelené do troch lekcií. Informácie v jednotlivých častiach sa prekrývajú a zakaždým sú podané trochu iným spôsobom. Umožní tak nahliadnuť na problematiku z rôznych uhlov.
V prvej časti sme si vysvetlili indexovanie a prácu s bunkami. Táto časť sa dopodrobna zaoberá tvorbou vektorov, prístupom k jednotlivým prvkom a ich filtrovaním. Podobné témy sú vysvetlené v tretej časti, len s pomocou matíc a na praktické ukážke úpravy fotografie.
Matice
Druhou nevyhnutnou záležitosťou k ovládnutiu MATLABu je práca s
maticami. Jeden príklad za všetky: Máme definované dvojrozmerné pole
(maticu). Úlohou je ku každému prvku poľa pripočítať hodnotu
20
.
Ak viete programovať v inom jazyku, možno by vás napadlo použiť cykly. Napr. v C # by sme to urobili takto:
var pole = new int[,]{ {5, 6},{9, 8},{6, 1} }; for(int k=0;k < pole.GetLength(0);k++) for(int l=0;l < pole.GetLength(1);l++) pole[k,l]+=20;
Ekvivalentom v MATLABe ale je:
pole = [5 6; 9 8; 6 1]; pole = pole + 20;
Žiadny dvojitý cyklus, iba jednoduché pripočítanie. MATLAB tu pochopil,
že ku každému prvku z matice chceme pripísať 20
. Ak by sme to
urobili takto: pole = pole + [10 20]
, teda snažili sa priradiť k
matici vektor, zahlási nám prostredie chybu, pretože už nevie, ako si s tým
poradiť (čo by sa tiež dalo čakať).
Typovanie
MATLAB je slabo typizovanom jazyk (rovnako ako napr. JavaScript alebo Python), nemusíme
mu definovať, že číslo je int
alebo double
. Táto
vlastnosť je skvelá pre rýchle písanie skriptov. Pri tvorbe komplexnejšieho
programu trochu prekáža, respektíve nepohlídá za nás niektoré hlúpe
chyby. Môžeme totiž napísať niečo ako:
promenna = 21; promenna = "teď je tu string a nikdo si nestěžuje"
Nikto si síce nesťažuje, ale až sa o pár desiatok riadkov kódu ďalej budeme snažiť pripočítať k premennej nejakej číslo, bude to už problém. A pre zistenie jeho príčiny sa budeme musieť prehrýzť o tie desiatky riadkov späť (a nevieme ako moc späť).
Tvorba vektorov
Po teoretickej odbočke späť k maticiam. Z definície premennej
pole = [5 6; 9 8; 6 1];
je zreteľné, ako sa definuje matice. Je
to teda v hranatých zátvorkách. Oddeľovačom čísel je medzera (alebo
čiarka), oddeľovačom riadkov je bodkočiarka. Finálny bodkočiarka je tu
preto, aby sa zamedzilo vypísanie do konzoly.
Ak chceme vytvoriť vektor obsahujúci čísla napríklad o 1
do
100
, možno to pomocou dvojbodky:
vektor = 1:100;
Skúsme si zahrnúť každé druhé číslo od 1
do
100
:
vektor = 1:2:100;
Vložením ďalšieho čísla medzi dvojbodky sa teda definuje krok. Funguje to aj s desatinnými číslami:
vektor = 1:0.1:100;
alebo bez tej nuly:
vektor = 1:.1:100;
Funguje to tiež pospiatky, len je nutné krokovať zápornou hodnotou:
vektor = 100:-.5:1;
Prístup k prvkom vektora
Ak chceme zistiť, čo sa vo vektore skrýva, teda pristúpiť k prvkom
poľa, využijeme index a okrúhle zátvorky. Tu priraďujeme do premennej
v
dvadsiatej číslo vektora:
v = vektor(20)
Ekvivalentom v C# by bolo:
int v = vektor[19];
Áno, MATLAB, na rozdiel od väčšiny používaných jazykov,
indexuje od 1
. Ak chceme vytiahnuť viac prvkov z vektora, stačí
ho naindexovat vektorom. 20., 21., A 22. prvok vektora získame:
prvky20_22 = vektor([20 21 22]);
Tu je čiastočné vysvetlenie toho, prečo sa k prístupom prvkov nevyužívajú hranaté zátvorky - tie sa používajú k definícii vektorov a matíc.
Ak by prvkov bolo viac, nebudeme ich v indexácie vektora vypisovať všetky, ale zas využijeme dvojbodky:
prvky20_50 = vektor(20:50)
Tu už nepotrebujeme hranatú zátvorku, vektor sa prezradil práve onou dvojbodkou. Ďalším spôsobom, ako vytiahnuť prvky vektora, je pomocou binárneho vektora:
vektor = [1 5 6 9 8 3]; pouze_neco_z_vektoru = vektor([0 0 1 1 0 0])
Týmto zápisom hovoríme: Prvý prvok neber, druhý tiež nie, tretí áno,
štvrtý áno, piaty nie, šiesty tiež nie ... Výsledkom je
[6 9]
. Binárne vektor musia mať rovnakú dĺžku ako vektor
pôvodný (inak by to nedávalo zmysel). K čomu je to dobré?
Podmienky pri výbere prvkov vektora
Z vektora chceme iba čísla väčšie ako 6
:
vetsi_nez_6 = vektor(vektor > 6)
Výsledok je [9 8]
.
Tento na prvý pohľad trochu krkolomný zápis obsahuje dva kroky. Ten
vnútorný je tvorba binárneho vektora. Pýtame Ak sa, či je vektor väčšia
ako skalár (vektor > 6)
, MATLAB to pochopí a proiteruje
podmienkou celý vektor. Tam, kde je splnená, zaznamená jedničku, tam, kde
nie, nulu. No a tento binárny vektor je následne využitý na indexovanie
samotného vektora.
C# ekvivalentom bolo toto:
var vetsi_nez_6 = new List<int>(); for(int k = 0; k < pole.GetLength(0); k++) if(pole[k] > 6) vetsi_nez_6.Add(pole[k]);
Alebo zjednodušene prevodom na List
a za použitia LINQ:
var pole = new int[] { 1, 5, 6, 9, 8, 3 }; var vetsi_nez_6 = (new List<int>(pole)).Where(x => x > 6);
Niekoľkých podmienok pri výbere prvkov
Podmienky na získavanie prvkov z vektora sa pochopiteľne dajú kombinovať.
Ak chceme teda z vektora získať iba prvky väčšie ako 6
, ktoré
sú navyše párne, použijeme nasledujúce zápis:
vetsi_nez_6_sude = vektor((vektor > 6) & (~mod(vektor, 2)))
Prvá časť výrazu už poznáme, za ňou nasleduje logické AND (teda
jednotka vznikne iba v prípade, že na oboch stranách budú jednotky). Druhá
časť je zodpovedná za určenie, či je číslo vo vektora párne. Deje sa tak
s pomocou funkcie mod()
(MATLAB nemá operátor modulo, ako
napríklad C# má %
. Percento je v MATLABe používané pre
komentára). Modulo nejakého čísla je zvyšok po delení a môže nadobúdať
akékoľvek hodnoty od 0
do čísla - 1. V prípade, že "moduluje"
s pomocou dvojky, sú možnosti buď 0
alebo 1
. Tu sa
prejavuje výhoda slabé typovosti. Výstupom funkcie mod()
je v
tomto prípade iba binárne vektor. Vlnovka ~
je operátorom
negácie (v C# výkričník !
). Využívame ju preto, aby sa z
jedničiek, ktoré vzniknú po "modulovania" dvojkou u nepárnych čísel, stali
nuly, a tým pádom výsledné číslo nebolo vybrané.
Rovnaké pravidlá ako pre indexovanie vektorov platí aj pre indexovanie
matíc. Ak chceme získať z matice M
iba prvky, ktoré sú
deliteľné 3
, urobíme to nasledovne:
M = [1 3 6 8; 5 6 9 3; 2 1 2 9]; delitelne_3 = M(mod(M, 3)==0) % 1. řešení delitelne_3_alt = M(~mod(M, 3)) % 2. řešení
Tu sme vytvorili 2 riešenie, prvý porovnáva výsledok modula s nulou,
ktorá vznikne v prípade deliteľnosti tromi. Alternatívne zápis využíva
opäť operátora negácie. Ten zo všetkých čísel, ktoré nie sú
0
, urobí práve 0
. Výsledky sú rovnaké.
Zápis je úplne totožný tomu, ako by sme operáciu vykonávali s vektormi. Výstupom je tiež vektor, tiež z toho dôvodu, že vopred nevieme, aký výsledok bude a či by sa vôbec do matice poskladal (ako vytvoríte dvojrozmernú maticu o troch prvkoch ...).
MATLAB iteruje maticou podobne ako vektorom. Nemusíme k tomu používať
cykly a tento funkcionálne a maticový prístup je odporúčaný spôsob
práce. Tiež je to spôsob, ktorý mnohým ľuďom robí problém riadne
uchopiť, zvlášť keď za všetkým vidia cyklus (a on tam v skutočnosti je).
Avšak komu sa to podarí, má nad Matlab napoly vyhraté. A nielen to.
Funkcionálne prístup sa premieta v mnohých iných jazykoch: R, Haskell,
Scala, F #, a mnohé ďalšie. Konieckoncov aj C #, predovšetkým od svojej 7.
verzie, pridáva spôsoby, ako riešiť problémy funkcionálne (napríklad
vyššie spomínaný kód pre filtrovanie čísiel väčších než
6
).
Nahradenie prvkov spĺňajúcich podmienku
Už vieme, ako z vektora (alebo matica) získame prvky spĺňajúce podmienku. Teraz tieto prvky nahradíme iným číslom:
vektor = [1 5 6 9 8 3]; vektor(vektor > 6) = 0;
Týmto kusom kódu sme čísla presahujúcej 6
nahradili nulou.
Výber prvkov sa nám presunul na ľavú stranu rovná sa.
Editácia prvkov spĺňajúcich podmienku
Predchádzajúci príklad čísla úplne nahradil, teraz prvky len trochu upravíme:
vektor(vektor > 6) = vektor(vektor > 6) + 3;
Je to zas kombinácia už známych postupov. Podmienka je na oboch stranách. Tento zápis by som osobne najradšej zjednodušil nasledovne:
%Tento kód není validní vektor(vektor > 6) +=3;
tak, ako to poznáme z mnohých iných jazykov. Avšak v MATLABe to nedá. Musíme sa teda uspokojiť s podmienkou na oboch stranách. Odporuje to princípu DRY (do not repeat yourself). Môžeme s tým vykonať len toto:
podminka = vektor > 6; vektor(podminka) = vektor(podminka) + 3;
Kód nevyzerá moc elegantnejšie, avšak ak je podmienka náročnejšie
(dlhšia), oplatí sa ju takto odstrčiť do premennej. podminka
bude obsahovať len 0
a 1
.
Cvičenie
Na záver nasleduje pár príkladov, ktorých riešenie nájdete na konci priloženého súboru. Súbor obsahuje aj všetok kód z tohto článku.
Vytvorte vektor f
s celými číslami od 101
do
202
az neho získajte:
- prvý a posledný prvok.
- prvých 12 prvkov.
- všetky prvky deliteľné
9
. - každý 4. prvok.
- vektor, ktorého hodnoty majú polovičnú hodnotu.
- vektor, ktorého hodnoty sú o 7 väčšie.
- posledných 13 prvkov.
- posledných 10 prvkov deliteľných 2.
- siedmy až devätnásty prvok.
- prvky deliteľné piatimi, ktorých posledný číslovka nie je
0
.
Nabudúce, v lekcii , sa podrobnejšie pozrieme na matice a konečne si povieme, k čomu je to všetko dobré
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 14x (1.3 kB)
Aplikácia je vrátane zdrojových kódov