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
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
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))
True
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
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
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
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
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)
<class 'sage.rings.rational.Rational'>
show(a + c)
type(a + c)
<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
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)