Práce s maticemi a vektory II
Způsob práce s vektory a maticemi v MATLABu je natolik zásadní téma, že je rozděleno do třech lekcí. Informace v jednotlivých částech se překrývají a pokaždé jsou podány trochu jiným způsobem. Umožní tak nahlédnout na problematiku z různých úhlů.
V první části jsme si vysvětlili indexování a práci s buňkami. Tato část se dopodrobna zabývá tvorbou vektorů, přístupem k jednotlivým prvkům a jejich filtrováním. Podobná témata jsou vysvětlena v třetí části, pouze s pomocí matic a na praktické ukázce úpravy fotografie.
Matice
Druhou nezbytnou záležitostí k ovládnutí MATLABu je práce s maticemi.
Jeden příklad za všechny: Máme definované dvourozměrné pole (matici).
Úkolem je ke každému prvku pole přičíst hodnotu 20
.
Pokud umíte programovat v jiném jazyce, možná by vás napadlo použít cykly. Např. v C# bychom to udělali 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;
Ekvivalentem v MATLABu ale je:
pole = [5 6; 9 8; 6 1]; pole = pole + 20;
Žádný dvojitý cyklus, pouze jednoduché přičtení. MATLAB zde pochopil,
že ke každému prvku z matice chceme přičíst 20
. Pokud bychom
to udělali takto: pole = pole + [10 20]
, tedy snažili se
přiřadit k matici vektor, zahlásí nám prostředí chybu, protože už
neví, jak si s tím poradit (což by se také dalo čekat).
Typování
MATLAB je slabě typovaný jazyk (stejně jako např. JavaScript nebo Python), nemusíme mu
definovat, že číslo je int
nebo double
. Tato
vlastnost je skvělá pro rychlé psaní skriptů. Při tvorbě
komplexnějšího programu trochu překáží, respektive nepohlídá za nás
některé hloupé chyby. Můžeme totiž napsat něco jako:
promenna = 21; promenna = "teď je tu string a nikdo si nestěžuje"
Nikdo si sice nestěžuje, ale až se o pár desítek řádků kódu dále budeme snažit přičíst k proměnné nějaké číslo, bude to již problém. A pro zjištění jeho příčiny se budeme muset prokousat o ty desítky řádků zpět (a nevíme jak moc zpět).
Tvorba vektorů
Po teoretické odbočce zpět k maticím. Z definice proměnné
pole = [5 6; 9 8; 6 1];
je zřetelné, jak se definuje matice. Je
to tedy v hranatých závorkách. Oddělovačem čísel je mezera (nebo
čárka), oddělovačem řádků je středník. Finální středník je zde
proto, aby se zamezilo vypsání do konzole.
Chceme-li vytvořit vektor obsahující čísla například o 1
do 100
, lze to pomocí dvojtečky:
vektor = 1:100;
Zkusme si zahrnout každé druhé číslo od 1
do
100
:
vektor = 1:2:100;
Vložením dalšího čísla mezi dvojtečky se tedy definuje krok. Funguje to i s desetinnými čísly:
vektor = 1:0.1:100;
nebo bez té nuly:
vektor = 1:.1:100;
Funguje to též pozpátku, jen je nutné krokovat zápornou hodnotou:
vektor = 100:-.5:1;
Přístup k prvkům vektoru
Chceme-li zjistit, co se ve vektoru skrývá, tedy přistoupit k prvkům
pole, využijeme index a kulaté závorky. Zde přiřazujeme do proměnné
v
dvacáté číslo vektoru:
v = vektor(20)
Ekvivalentem v C# by bylo:
int v = vektor[19];
Ano, MATLAB, na rozdíl od většiny používaných jazyků,
indexuje od 1
. Chceme-li vytáhnout více prvků z vektoru, stačí
ho naindexovat vektorem. 20. ,21., a 22. prvek vektoru získáme:
prvky20_22 = vektor([20 21 22]);
Zde je částečné vysvětlení toho, proč se k přístupům prvků nevyužívají hranaté závorky – ty se používají k definici vektorů a matic.
Pokud by prvků bylo více, nebudeme je v indexačním vektoru vypisovat všechny, ale zas využijeme dvojtečky:
prvky20_50 = vektor(20:50)
Zde již nepotřebujeme hranatou závorku, vektor se prozradil právě onou dvojtečkou. Dalším způsobem, jak vytáhnout prvky vektoru, je pomocí binárního vektoru:
vektor = [1 5 6 9 8 3]; pouze_neco_z_vektoru = vektor([0 0 1 1 0 0])
Tímto zápisem říkáme: První prvek neber, druhý také ne, třetí ano,
čtvrtý ano, pátý ne, šestý také ne... Výsledkem je [6 9]
.
Binární vektor musí mít stejnou délku jako vektor původní (jinak by to
nedávalo smysl). K čemu je to dobré?
Podmínky při výběru prvků vektoru
Z vektoru chceme pouze čísla větší než 6
:
vetsi_nez_6 = vektor(vektor > 6)
Výsledek je [9 8]
.
Tento na první pohled trochu krkolomný zápis obsahuje dva kroky. Ten
vnitřní je tvorba binárního vektoru. Tážeme-li se, zdali je vektor
větší než skalár (vektor > 6)
, MATLAB to pochopí a
proiteruje podmínkou celý vektor. Tam, kde je splněna, zaznamená jedničku,
tam, kde nikoliv, nulu. No a tento binární vektor je následně využit k
indexování samotného vektoru.
C# ekvivalentem bylo 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]);
Anebo zjednodušeně převodem na List
a za použití LINQu:
var pole = new int[] { 1, 5, 6, 9, 8, 3 }; var vetsi_nez_6 = (new List<int>(pole)).Where(x => x > 6);
Vícero podmínek při výběru prvků
Podmínky pro získávání prvků z vektoru se pochopitelně dají
kombinovat. Chceme-li tedy z vektoru získat pouze prvky větší než
6
, které jsou navíc sudé, použijeme následující zápis:
vetsi_nez_6_sude = vektor((vektor > 6) & (~mod(vektor, 2)))
První část výrazu již známe, za ní následuje logické AND (tedy
jednička vznikne pouze v případě, že na obou stranách budou jedničky).
Druhá část je zodpovědná za určení, zda je číslo ve vektoru sudé.
Děje se tak s pomocí funkce mod()
(MATLAB nemá operátor modulo,
jako třeba C# má %
. Procento je v MATLABu používáno pro
komentáře). Modulo nějakého čísla je zbytek po celočíselném dělení a
může nabývat jakékoliv hodnoty od 0
do čísla - 1. V
případě, že "modulujeme" s pomocí dvojky, jsou možnosti buď
0
nebo 1
. Zde se projevuje výhoda slabé typovosti.
Výstupem funkce mod()
je v tomto případě pouze binární
vektor. Vlnovka ~
je operátorem negace (v C# vykřičník
!
). Využíváme ji proto, aby se z jedniček, které vzniknou po
"modulování" dvojkou u lichých čísel, staly nuly, a tím pádem výsledné
číslo nebylo vybráno.
Stejná pravidla jako pro indexování vektorů platí i pro indexování
matic. Chceme-li získat z matice M
pouze prvky, které jsou
dělitelné 3
, uděláme to následovně:
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í
Zde jsme vytvořili 2 řešení, první porovnává výsledek modula s nulou,
která vznikne v případě dělitelnosti třemi. Alternativní zápis
využívá opět operátoru negace. Ten ze všech čísel, která nejsou
0
, udělá právě 0
. Výsledky jsou stejné.
Zápis je zcela totožný tomu, jako bychom operaci prováděli s vektory. Výstupem je též vektor, také z toho důvodu, že předem nevíme, jaký výsledek bude a jestli by se vůbec do matice poskládal (jak vytvoříte dvourozměrnou matici o třech prvcích...).
MATLAB iteruje maticí podobně jako vektorem. Nemusíme k tomu používat
cykly a tento funkcionální a maticový přístup je doporučený způsob
práce. Také je to způsob, který spoustě lidem dělá problém řádně
uchopit, zvlášť když za vším vidí cyklus (a on tam ve skutečnosti je).
Nicméně komu se to podaří, má nad MATLABem zpola vyhráno. A nejen to.
Funkcionální přístup se promítá v mnoha jiných jazycích: R, Haskell,
Scala, F#, a mnohé další. Koneckonců i C#, především od své 7. verze,
přidává způsoby, jak řešit problémy funkcionálně (například výše
zmíněný kód pro filtrování čísel větších než 6
).
Nahrazení prvků splňujících podmínku
Již víme, jak z vektoru (nebo matice) získáme prvky splňující podmínku. Nyní tyto prvky nahradíme jiným číslem:
vektor = [1 5 6 9 8 3]; vektor(vektor > 6) = 0;
Tímto kusem kódu jsme čísla přesahující 6
nahradili
nulou. Výběr prvků se nám přesunul na levou stranu rovnítka.
Editace prvků splňujících podmínku
Předchozí příklad čísla zcela nahradil, nyní prvky jen trochu upravíme:
vektor(vektor > 6) = vektor(vektor > 6) + 3;
Je to zas kombinace již známých postupů. Podmínka je na obou stranách. Tento zápis bych osobně nejraději zjednodušil následovně:
%Tento kód není validní vektor(vektor > 6) +=3;
tak, jak to známe z mnoha jiných jazyků. Nicméně v MATLABu to nelze. Musíme se tedy spokojit s podmínkou na obou stranách. Odporuje to principu DRY (don't repeat yourself). Můžeme s tím provést jen toto:
podminka = vektor > 6; vektor(podminka) = vektor(podminka) + 3;
Kód nevypadá moc elegantněji, avšak pokud je podmínka náročnější
(delší), vyplatí se ji takto odstrčit do proměnné. podminka
bude obsahovat pouze 0
a 1
.
Cvičení
Na závěr následuje pár příkladů, jejichž řešení naleznete na konci přiloženého souboru. Soubor obsahuje i veškerý kód z tohoto článku.
Vytvořte vektor f
s celými čísly od 101
do
202
a z něj získejte:
- první a poslední prvek.
- prvních 12 prvků.
- všechny prvky dělitelné
9
. - každý 4. prvek.
- vektor, jehož hodnoty mají poloviční hodnotu.
- vektor, jehož hodnoty jsou o 7 větší.
- posledních 13 prvků.
- posledních 10 prvků dělitelných 2.
- sedmý až devatenáctý prvek.
- prvky dělitelné pěti, jejichž poslední číslovka není
0
.
Příště, v lekci , se podrobněji podíváme na matice a konečně
si řekneme, k čemu je to všechno dobré
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 14x (1.3 kB)
Aplikace je včetně zdrojových kódů