2. Číselné množiny a důležité konstanty#

Výchozím bodem pro nás bude práce s číselnými objekty. Pozor, SageMath ve většině případů nepoužívá obyčejné Python datové typy (jako int a float).

2.1 Celá čísla#

Celá čísla lze v počítačových algebraických systémech reprezentovat přesně a provádět s nimi algebraické operace sčítání a násobení bez chyb, dokud stačí paměť. Objekt odpovídající množině celých čísel (z pohledu matematika se jedná o okruh (ring)) je v SageMath značen ZZ (viz také dokumentaci).

print(ZZ)
show(ZZ)
Integer Ring
\(\displaystyle \newcommand{\Bold}[1]{\mathbf{#1}}\Bold{Z}\)

Celá čísla lze konstruovat několika ekvivalentními způsoby.

# ekvivalentní způsoby vytvoření obektu typu SageMath Integer
j = 1
k = ZZ(1)
l = Integer(1)

print(j == k)
print(k == l)
print(type(j))
True
True
<class 'sage.rings.integer.Integer'>

SageMathovské celé číslo je daleko bohatší objekt, než Pythonovský integer (ten zkonstruujete například příkazem int(2)). Na celém čísle můžete provádět celou řadu operací, například:

k = 123475

print(k.binary())
print(k.factor())
print(k.divisors())
11110001001010011
5^2 * 11 * 449
[1, 5, 11, 25, 55, 275, 449, 2245, 4939, 11225, 24695, 123475]

Metody a vlastnosti můžete prozkoumat stiskem TAB za tečkou, případně také viz stránku v dokumentaci.

Přirozená čísla lze sčítat +, odčítat - a násobit *.

10_000 + 500
10500
123 - 321
-198
10_000_000_000 * 999
9990000000000

K dělení / se SageMath chová jinak, než jste zvyklí s Pythonu. Dělení celých čísel, které lze provést beze zbytku nevyústí v hodnotu jiného typu (číslo s plovoucí desetinnou čárkou, float), ale stále celé číslo:

10 / 5
2

Pokud dělíme celá čísla, které nelze dělit bezezbytku, pak dostaneme racionální číslo (ne float[1])! SageMath (a i další počítačové algebraické systémy) se tak narozdíl od řady programovacích prostředí snaží co nejdéle držet správnost výpočtů.

2.2 Racionální čísla#

Podobně jako s celými čísly lze pracovat i s racionálními čísly s absolutní přesností (dokud vystačíme s pamětí). Racionálním číslům odpovídá objekt onačovaný jako QQ (viz dokumentace). Racionální čísla již tvoří těleso (anglicky field).

print(QQ)
show(QQ)
Rational Field
\(\displaystyle \newcommand{\Bold}[1]{\mathbf{#1}}\Bold{Q}\)

Racionální čísla (viz dokumentaci) lze opět vytvářet několika různými způsoby (dělením celých čísel přirozeně vzniká objekt typu racionální číslo). Algebraickými operacemi sčítání +, odčítání -, násobení * či dělení / již množinu racionálních čísel neopustíme.

Všimněte si, že podíl dvou celých čísel, kde první není dělitelné druhým, přirozeně vyústí v racionální hodnotu.

p = 1 / 2
q = QQ(1 / 2)

show(p)
print(q == p)
show(p^10 * 3^2 / q^(-21))
\(\displaystyle \frac{1}{2}\)
True
\(\displaystyle \frac{9}{2147483648}\)

Pozor, výraz 1 / 2. (tečka zde není překlepem), kde jsme ve jmenovateli použili strojové číslo, už vám racionální číslo v tomto smyslu nevytvoří, sklouznete do nepřesné aritmetiky strojových čísel, viz níže.

1/2.
0.500000000000000

2.3 „Reálná“ čísla (float)#

Stručně lze říci, že v SageMath jsou čísla s pohyblivou desetinnou čárkou s přesností \(p\) chápána jako čísla tvaru

\[ sm2^{e-p} \]
kde \(s\in\{-1,1\}\) je znaménko, \(2^{p-1} \leq m < 2^{p}\) je mantisa a \(-2^{30} +1 \leq e < 2^{30} - 1\) je exponent. Obě \(m\) i \(e\) jsou nezáporná celá čísla. Defaultně SageMath pracuje s těmito čísly v přesnosti \(p = 53\) (bitů pro signifikant), a reprodukuje tak \(64\) bitový typ double známý z C, ovšem s větším rozsahem exponentu. Další detaily týkající se těchto strojových čísel může čtenář dohledat v dokumentaci.

a = 2.7

print(type(a))
print(a.precision())
<class 'sage.rings.real_mpfr.RealLiteral'>
53

Tato tzv. strojová čísla v této pevné přesností tvoří pouze aproximaci tělesa reálných čísel. Ve skutečnosti se jedná pouze o konečnou podmnožinu možiny racionálních čísel! Nejsou ani uzavřená na algebraické operace, při výpočtech i vytváření aproximace vstupu dochází k zaokrouhlování a tedy chybám. Tyto chyby mohou mít při kumulaci fatální následky. SageMath tato čísla značí symbolem RR (viz dokumentaci).

print(RR)
show(RR)
Real Field with 53 bits of precision
\(\displaystyle \newcommand{\Bold}[1]{\mathbf{#1}}\Bold{R}\)

Použití slovíčka field zde z matematického pohledu není správné. Pokud chceme, můžeme experimentovat i s menší či větší přesností (počet bitů pro signifikant).

F = RealField(10)
a = F(1.3)

print(a)
print(a.precision())
1.3
10

Jaký exponent a signifikant odpovídají takovémuto číslu?

# jako exponent již vrací $e-p$
sign, mantisa, exponent = a.sign_mantissa_exponent()
print(sign, ", ", mantisa, ", ", exponent)
1 ,  666 ,  -9

Z těchto údajů zrekonstruujeme zpětně naše číslo \(a\):

sign * mantisa * 2^exponent
333/256

Tj. v počítači v tomto případě není uložené číslo 1.3, tj. \(\frac{13}{10}\), ale následující číslo s konečným desetinným rozvojem (což není nutné):

n(333 / 256, digits=50)
1.3007812500000000000000000000000000000000000000000

Podobně, např. pro 0.17 máme:

a = RR(0.17)
sign, mantisa, exponent = a.sign_mantissa_exponent()

print('znaménko, mantisa, exponent')
print(sign, ', ', mantisa, ', ', exponent)

print('\nodpovídající racionální číslo')
exact = sign * mantisa * 2^exponent
show(exact)

print('\njeho přibližná numerická hodnota (20 cifer)')
print(n(exact, digits=20))
znaménko, mantisa, exponent
1 ,  6124895493223875 ,  -55

odpovídající racionální číslo
\(\displaystyle \frac{6124895493223875}{36028797018963968}\)
jeho přibližná numerická hodnota (20 cifer)
0.17000000000000001221

Tohoto problému se nezbavíme zvýšením přesnosti, je inherentní konstrukci strojových čísel, která jsou uložená v binárním formátu. Podívejme se na stejný případ s podstatně "přesnějším" strojovým číslem:

F = RealField(512)
a = F(0.17)

print('přesnost')
print(a.precision())

print('\nznaménko, mantisa, exponent')
sign, mantisa, exponent = a.sign_mantissa_exponent()
print(sign, ', ', mantisa, ', ', exponent)

print('\nodpovídající strojové číslo')
exact = sign * mantisa * 2^exponent
show(exact)

print('\nnumerická hodnota')
print(n(exact, digits=240))
přesnost
512

znaménko, mantisa, exponent
1 ,  9117309392360966027710336998779975366685968758002827496852021781730799540450011944225274522753494330829221663566810514580552640312123667563574881324137185 ,  -514

odpovídající strojové číslo
\(\displaystyle \frac{9117309392360966027710336998779975366685968758002827496852021781730799540450011944225274522753494330829221663566810514580552640312123667563574881324137185}{53631231719770388398296099992823384509917463282369573510894245774887056120294187907207497192667613710760127432745944203415015531247786279785734596024336384}\)
numerická hodnota
0.169999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999994779161488159855279696324279175946313836469925779715174009937166755605130450917186545

Při práci s čísly s plovoucí desetinnou čárkou musíme být opatrní. Kvůli problémům způsobeným nepřesnostmi nejsou některé algoritmy, resp. jejich implementace ve strojových číslech, prakticky použitelné (např. Gaussova eliminace)

Na závěr této sekce o reálných číslech poznamenejme, že Eulerovo číslo se v SageMath označuje symbolem e a Ludolfovo číslo symbolem pi. Sage s nimi pracuje jako s abstraktními hodnotami, teprve v rámci výpočtu dle situace využije vhodnou aproximaci.

e # Eulerovo číslo
e
pi # Ludolfovo číslo
pi
n(e, digits=500)
2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274274663919320030599218174135966290435729003342952605956307381323286279434907632338298807531952510190115738341879307021540891499348841675092447614606680822648001684774118537423454424371075390777449920695517027618386062613313845830007520449338265602976067371132007093287091274437470472306969772093101416928368190255151086574637721112523897844250569536967707854499699679468644549059879316368892300987931

Metoda n (nebo ekvivalentně N) umožňuje nalézt decimální vyjádření výrazu na požadovaný počet cifer.

n(pi, digits=500)
3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491

2.4 „Komplexní“ čísla#

S komplexními čísly se v SageMath pracuje poměrně jednoduše, jde o dvojici reálných čísel ve smyslu uvedeném v předchozí části notebooku. Imaginární jednotka je označena následovně

print(I)
type(I)
I
<class 'sage.rings.number_field.number_field_element_quadratic.NumberFieldElement_gaussian'>

Například dle očekávání platí:

I^2
-1

Všechny očekávané algebraické operace lze provádět i s komplexními čísly.

(1+2*I)*(1 - I)
I + 3

Komplexní čísla v BI-MA1 podrobněji využívat nebudeme, proto se jim zde nebudeme hlouběji věnovat.

2.5 Interakce mezi typy (přetypování)#

SageMath se k operacím mezi různými typy čísel chová poměrně očekávaně, z matematického úhlu pohledu. Symbolickým výrazům se budeme věnovat v dalším notebooku, teď máme opravdu na mysli operace mezi instancemi různých číselných typů zmíněné v tomto notebooku.

Pojďme se podívat na sčítání celých a racionálních čísel.

a = ZZ(2)   # celé číslo
b = QQ(3)   # formálně racionální číslo, ale s celočíselnou hodnotou
c = 4 / 5   # racionální číslo, které není celé
show(a + b)
type(a + b)
\(\displaystyle 5\)
<class 'sage.rings.rational.Rational'>
show(a + c)
type(a + c)
\(\displaystyle \frac{14}{5}\)
<class 'sage.rings.rational.Rational'>

Jakmile ve se ve výrazu vyskytne strojové číslo, tak celý výpočet už proběhne v aritmetice strojových čísel.

2.0 + 4 / 5
2.80000000000000
4.0 / 2
2.00000000000000

Na toto pozor. Pokud chceme pracovat přesně, exaktně, tak bychom se strojovým číslům měli vyhnout.

2.6 Dodatek#

K algebraickému výrazu \(0 ^ 0\) se SageMath chová jako k jedničce, nezávisle na typu (pozor, k umocňování lze použít znak ^, který má bliže matematické notaci než Pythonovské **):

0 ^ 0  # celá čísla
1
0.0 ^ 0.0 # strojová čísla
1.00000000000000
QQ(0) ^ QQ(0) # racionální čísla
1

Racionální čísla, reálná čísla a komplexní čísla tvoří spolu se standardními algebraickými operacemi těleso. Další typ tělesa, který znáte z BI-LA1 jsou konečná tělesa \(\mathbb{Z}_p\), resp. v alternativní notaci \(GF(p)\).

Pro prvočíslo \(p\) jde o množinu \(\{0,1,2,\ldots,p-1\}\) vybavenou sčítáním a násobením modulo \(p\). Tato tělesa mají \(p\) prvků, proto jsme použili slovíčko konečná. V SageMath s nimi můžeme pracovat velmi přirozeně. Podobně jako ZZ, QQ a RR, nejprve vytvoříme objekt odpovídající danému tělesu s \(p = 7\):

Z7 = GF(7)

print(Z7)
show(Z7)
Finite Field of size 7
\(\displaystyle \newcommand{\Bold}[1]{\mathbf{#1}}\Bold{F}_{7}\)

Jak vidíme, SageMath používá malinko alternativní notaci.

list(Z7)
[0, 1, 2, 3, 4, 5, 6]

Sčítání a násobení funguje bezešvě:

Z7(3) * Z7(6)
4
Z7(3) + Z7(6)
2
Z7(3) - Z7(6)
4
-Z7(6)
1

Při operaci s "obyčejným" celým číslem se použijí operace z \(\mathbb{Z}_7\):

3 + Z7(6)
2
2 * Z7(5)
3

Výpočet inverzí v \(\mathbb{Z}_7\):

for x in Z7:
    if x == 0:
        continue
        
    print("Inverze k", x, "v Z7: ", x ^ (-1))
Inverze k 1 v Z7:  1
Inverze k 2 v Z7:  4
Inverze k 3 v Z7:  5
Inverze k 4 v Z7:  2
Inverze k 5 v Z7:  3
Inverze k 6 v Z7:  6

V SageMath lze i velmi elegantně pracovat s konečnými tělesy majícími \(p^n\) prvků (\(p\) prvočíslo). Případně lze krásně pracovat i s maticemi a vektory s prvky z takovýchto konečných těles. Tomu se zde ale podrobněji věnovat nebudeme, zde cílíme na použití SageMath v matematické analýze.

T = GF(3^5)
T
Finite Field in z5 of size 3^5
m = matrix(GF(5), [
    [3, 2],
    [1, 0]
])

show(m * m + m)
\(\displaystyle \left(\begin{array}{rr} 4 & 3 \\ 4 & 2 \end{array}\right)\)