Tomáš Kalvoda, KAM FIT ČVUT, 2019
Tento notebook ukazuje jak v Sage pracovat s maticemi s prvky z různých těles (resp. nejen těles).
Připomeňme si nejprve základní pojmy. Mějme těleso a přirozená čísla . Potom , tedy zobrazení, které dvojici indexů přiřadí prvek z (ozn. ), nazýváme maticí s prvky z tělesa .
Matice si můžeme přirozeně představovat jako tabulku čísel indexovanou pomocí dvou indexů a . Konvečně se prvek klade do tého řádku a tého sloupce. Prvek je v levém horním rohu, v levém dolním rohu, v pravém dolním rohu a v pravém horním rohu, konkrétně S přimhouřenýma očima bychom o maticích z programátorského pohledu mohli mluvit jako o dvourozměrných polích. Občas se také samotné prvky matice značí malým latinským písmenkem, tj. mohli bychom psát .
Indexu se říká řádkový index, indexu sloupcový index. Pokud platí , pak mluvíme o čtvercových maticích, jinak o maticích obdélníkových.
Často je potřeba mluvit o a hlavně pracovat s jednotlivými řádky a sloupci matic. K tomu zavádíme jednoduché značení kompatibilní s Pythonem. Pokud budeme mluvit o matici zmíněné výše, tak pod symbolem máme na mysli její tý řádek, tj. a pod symbolem pak její tý sloupec, tj.
K vytváření matic v Sage vystačíme s Pythonovským polem, tzv. listem, a funkcí matrix
.
List (pole) v pythonu se vytváří jednoduše pomocí hranatých závorek a může jako své prvky obsahovat všechno možné:
a = [1, 2, 'a']
a
[1, 2, 'a']
V proměnné a
je uložen Pythonovský list, na který se můžete v tomto případě dívat jako na pole, indexované od a mající tři prvky.
K prvkům se přistupuje opět pomocí hranatých závorek:
print(a[0])
print(a[1])
print(a[2])
1 2 a
Matice v Sage nejjednodušeji zkonstruujeme pomocí funkce matrix
, které předáme list listů představující požadovanou matici zadanou po řádcích (vnitřní listy tedy odpovídají řádkům naší matice).
A = matrix([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
show(A)
S obyčejným listem bychom si pro práci s maticemi nevystačili.
Podívejte se, jaké metody jsou k dispozici pro a
a jaké pro A
.
S maticí v proměnné A
teď budeme moci provádět spoustu užitečených operací.
V úvodní části této sekce jsme nespecifikovali odkud máme prvky matice brát. Sage opět se v tomto případě snaží uhodnout, nad jakým tělesem se pohybujeme.
A.parent()
Full MatrixSpace of 3 by 3 dense matrices over Integer Ring
Tj. bez dalšího vysvětlování náš vstup chápe jako matici s prvky z množiny celých čísel , tedy jako prvek .
Ve většině případů není potřeba toto chování měnit, například v následujících případech je výsledek takový, jaký jsme zřejmě chtěli:
A = matrix([
[1/2, 2/3],
[4/5, 5/6]
])
show(A)
A.parent()
Full MatrixSpace of 2 by 2 dense matrices over Rational Field
A = matrix([
[0.5, 2.2],
[0.3, 1.1]
])
show(A)
A.parent()
Full MatrixSpace of 2 by 2 dense matrices over Real Field with 53 bits of precision
Občas chceme ale vynutit typ prvků matice, například když pracujeme nad konečným tělesem.
V tom případě je nejsnazší předat toto těleso jako první argument funkce matrix
.
Například:
A = matrix(GF(5), [
[2, 4],
[1, 3]
])
show(A)
A.parent()
Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 5
Na první pohled se nezdá, že se od prvního příkladu moc nezměnilo. Ale jak si ukážeme níže, výsledky různých maticových operací budu podstatně jiné, než kdybychom ji chápali jako prvek . Nyní jde o matici z .
Ukážeme si různé jednoduché způsoby jak přistupovat k prvkům matice A
definované hned nad tímto dostavcem.
Jednotlivé prvky dostaneme tak jak jsme si zavedli indexování (pozor, v Sage i Pythonu od )!
print('Prvky v prvním řádku:')
print(A[0, 0])
print(A[0, 1])
print('Prvky v druhém řádku:')
print(A[1, 0])
print(A[1, 1])
Prvky v prvním řádku: 2 4 Prvky v druhém řádku: 1 3
Nebo bychom mohli získat jednotlivé sloupce a řádky (to se nám bude hodit později).
print('První řádek:')
show(A[0,:])
print('Druhý sloupec:')
show(A[:,1])
První řádek:
Druhý sloupec:
Matice z můžeme přirozeně násobit číslem z tělesa . Pod maticí rozumíme matici, jejíž prvky jsou -násobky prvků matice , přesněji pro každé a .
V Sage této operaci odpovídá *
pokud nalevo máme číslo a napravo matici.
A = matrix(QQ, [
[1, 1/2],
[3, 3/5]
])
show(7 * A)
Důležité je uvědomit si, že operace násobení mezi prvky matice se provádí v tělese !
A = matrix(GF(7), [
[1, 3],
[2, 5]
])
show(2 * A)
Matice stejných rozměrů s prvky ze stejného tělesa můžeme přirozeně sčítat prvek po prvku, pro každé a .
V Sage této operaci odpovídá +
mezi maticemi stejných rozměrů.
Podobně bude přirozeně fungovat operátor -
.
A = matrix(QQ, [
[1, 1/2, 1/3],
[3, 2, 1]
])
B = matrix(QQ, [
[-1, -1, -1],
[1/2, 0, 1/2]
])
show(A + B)
Důležité je uvědomit si, že operace sčítání mezi prvky matice se provádí v tělese !
A = matrix(GF(7), [
[1, 2, 3],
[5, 5, 2]
])
B = matrix(GF(7), [
[6, 1, 1],
[2, 4, 3]
])
show(A + B)
Tato operace mezi maticemi může při prvním setkání vypadat poněkud komplikovaně.
Mějme matice a . Tedy má stejně sloupců jako řádků. Součinem těchto dvou matic je matice jejíž prvky jsou dány následovně pro každé a .
Pokud se nad uvedenou sumou zamyslíme, tak vidíme, že prvek součinu na tém řádku a tém sloupci vznikl tak, že jsme vzali tý řádek matice a tý řádek matice (tyto mají stejný počet prvků), vynásobili jsme je člen po členu (vnitřek sumy) a součiny sečetli (suma).
V tento okamžik se tato operace může zdát velmi "libovolná". Později během semestru si ukážeme, jak se k ní lze velmi přirozeně dostat při studiu lineárních zobrazení a jejich skládání.
V Sage této operaci odpovídá operátor *
mezi maticemi správných rozměrů.
Vezměmě konkrétní příklad.
A = matrix(QQ, [
[1, 1/2, 1/3],
[3, 2, 1]
])
B = matrix(QQ, [
[-1, -1, -1],
[1/2, 0, 1/2],
[3, 4, 5]
])
show(A)
show(B)
print('Součin:')
show(A * B)
Součin:
Pojďme si tento výsledek podrobněji rozebrat a spočtěme prvek v prvním řádku a druhém sloupci.
Ten by měl vzniknout tak, že vezme první řádek matice A
a druhý sloupec matice B
,
show(A[0, :])
show(B[:, 1])
vynásobíme je prvek po prvku a výsledky sečteme, dostaneme tak očekávaný výsledek
sum(A[0, j] * B[j, 1] for j in range(3)) # range(3) = {0, 1, 2}, více méně
1/3
Poznámka: Speciálně platí, že čtvercové matice stejného rozměru můžeme násobit dle libosti. Pro opakované násobení se přirozeně používá zkrácené značení ( matic vynásobených samo se sebou).
A = matrix([
[1, 2],
[3, 4]
])
show(A^5)
Poznámka: Další operací, kterou s maticemi lze provádět je inverze. Té se budeme věnovat později během semestru.
Poznámka: Operace násobení i sčítání matic jsou asociativní. Distributivita maticového násobení vůči sčítání také platí. Násobení matic ovšem není komutativní!!!
Následující výpočet není důkaz těchto tvrzení, pouze demonstrace o čem mluvíme.
A = matrix(QQ, [
[1, 3],
[1, 2]
])
B = matrix(QQ, [
[1, -3],
[3, 0]
])
C = matrix(QQ, [
[0, -1],
[1, 0]
])
print('A + (B + C) == (A + B) + C:')
print(A + (B + C) == (A + B) + C)
print('A * (B * C) == (A * B) * C:')
print(A * (B * C) == (A * B) * C)
print('A * (B + C) == (A * B) + (A * C):')
print(A * (B + C) == (A * B) + (A * C))
print('A * B != B * A:')
print(A * B == B * A)
A + (B + C) == (A + B) + C: True A * (B * C) == (A * B) * C: True A * (B + C) == (A * B) + (A * C): True A * B != B * A: False
Opravdu, součiny těchto dvou matic jsou:
show(A * B)
show(B * A)
V tomto dodatku probereme pár užitečných způsobů jak konstruovat různé matice.
Matice s dvěma řádky a čtyřmi sloupci s náhodně zvolenými prvky z tělesa .
show(random_matrix(GF(3), 2, 4))
Náhodná matice stejného rozměru, pouze s prvky zvolenými z tělesa racionálních čísel .
show(random_matrix(QQ, 2, 4))
Nulová matice (matice se všemi prvky nulovými) s čtyřmi řádky a třemi sloupci.
show(zero_matrix(4, 3))
Matice rozměru se všemi prvky rovnými .
show(matrix(4, 4, 2))
matrix?
Jednotková matice (matice s jedničkami na diagonále a nulami všude jinde) rozměru .
show(identity_matrix(5, 5))
Uvedené funkce umožňují i další možnosti jak matice konstruovat. Zvídavý čtenář se může inspirovat v dokumentaci. Alespoň jeden zajímavý netriviální příklad si uveďme.
f = lambda i, j: 1/(1 + i + j) # funkce dvou proměnných, f(i, j) = 1/(1 + i + j)
m = matrix(QQ, 5, 5, f) # matice mající prvky zkonstruované pomocí uvedené funkce
show(m)