Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupport News AboutSign UpSign In
| Download
Views: 61

Konečná tělesa a jejich rozšíření

Tomáš Kalvoda & Štěpán Starosta, KAM FIT ČVUT, 2014-2017

V tomto worksheetu shrneme různé užitečné nástroje pro práci s konečnými tělesy v SageMath.

Příkazy pro konstrukci konečného tělesa

V SageMath máme k dispozici příkaz GF (alias pro FiniteField). Chceme-li například pracovat v konečném tělese s prvočíselným počtem prvků, můžeme toto těleso ihned vytvořit následujícím příkazem.

p = random_prime(1000, lbound=200) # zvolime si nahodne prvocislo vetsi nez 200 a mensi nez 1000 T = GF(p) T show(T)
Finite Field of size 919
F919\displaystyle \Bold{F}_{919}

V GF(p)GF(p) se sčítá a násobí modulo pp. Počítání v tomto konečném tělese funguje jak by člověk očekával.

a = T(127) # dva testovaci prvky naseho telesa $T$ b = T(199) print "Soucin" a * b print "Soucet" a + b print "Sta mocnina prvku $a$" a ^ 100 print "Inverze prvku $a$" c = a^-1 c print "Test inverze" a * c
Soucin 460 Soucet 326 Sta mocnina prvku $a$ 23 Inverze prvku $a$ 398 Test inverze 1

Dále nám SageMath umožňuje konstruovat konečná tělesa s řádem, který je neprvočíselný, tedy mocninou nějakého prvočísla pp. Již víme jak tato tělesa konstruovat pomocí faktorizace. Nyní si ukážeme možnosti funkce GF. Zkonstruujme těleso řádu pnp^n, kde pp jsme si zvolili dříve a n=5n = 5.

n = 5 T.<x> = GF(p^n) T show(T)
Finite Field in x of size 919^5
F9195\displaystyle \Bold{F}_{919^{5}}

Pozor! Přikonstrukci tělesa neprvočíselného řádu je nutné SageMath říct, jak budeme označovat proměnnou příslušných polynomů. (Tomuto prvku se v SageMath také říká generátor, není to ovšem (obecně) generátor multiplikativní grupy!) Pokud Vám syntax v předchozí buňce přijde podivná, lze použít alternativní zápis:

T = GF(p^n, name='x') T
Finite Field in x of size 919^5

Pokud SageMath neřekneme, v jakém modulu chceme pracovat, sám zvolí ireducibilní (nad GF(p)GF(p)) polynom požadovaného řádu nn. Jsme-li zvědaví, můžeme si tento polynom snadno zjistit.

T.modulus() # Ireducibilni polynom telesa v kterem pracujeme.
x^5 + x + 294

Podobně jako v předchozím příkladě (těleso prvočíselného řádu) se podívejme jak lze provádět algebraické operace v tomto konečném tělese. Nejprve zkonstuujme dva prvky tělesa TT.

a = 5*x^6 + 7*x^2 +1 # vsimnete si, ze Sage automaticky zvoli vhodneho reprezentanta z [a]. a b = T('7x+x^2') # dalsi mozny zpusob konstrukce prvku $T$ b type(b)
871*x^2 + 35*x + 1 x^2 + 7*x <type 'sage.rings.finite_rings.element_pari_ffelt.FiniteFieldElement_pari_ffelt'>

Poznamenejme, že v prvním případě SageMath pozná, že mluvíme o prvku TT, protože jsme použili 'x' k označení formální proměnné polynomů. Například následující pokusy zřejmě nefungují.

5*y # y neni definovano
Error in lines 1-1 Traceback (most recent call last): File "/projects/sage/sage-7.5/local/lib/python2.7/site-packages/smc_sagews/sage_server.py", line 982, in execute exec compile(block+'\n', '', 'single') in namespace, locals File "", line 1, in <module> NameError: name 'y' is not defined
T('y') # y neni formalni promenna polynomu, Sage # se proto proto y snazi prevest na koeficient (integer), ale # y neni vubec definovano # (bylo-li by y definovano a SageMath by znal nejaky rozumny zpusob, jak jej prevest na prvek z T, pokusi se o to)
Error in lines 1-1 Traceback (most recent call last): File "/projects/sage/sage-7.5/local/lib/python2.7/site-packages/smc_sagews/sage_server.py", line 982, in execute exec compile(block+'\n', '', 'single') in namespace, locals File "", line 1, in <module> File "sage/structure/parent.pyx", line 955, in sage.structure.parent.Parent.__call__ (/projects/sage/sage-7.5/src/build/cythonized/sage/structure/parent.c:9852) return mor._call_(x) File "sage/structure/coerce_maps.pyx", line 110, in sage.structure.coerce_maps.DefaultConvertMap_unique._call_ (/projects/sage/sage-7.5/src/build/cythonized/sage/structure/coerce_maps.c:4883) raise File "sage/structure/coerce_maps.pyx", line 105, in sage.structure.coerce_maps.DefaultConvertMap_unique._call_ (/projects/sage/sage-7.5/src/build/cythonized/sage/structure/coerce_maps.c:4750) return C._element_constructor(x) File "/projects/sage/sage-7.5/local/lib/python2.7/site-packages/sage/rings/finite_rings/finite_field_pari_ffelt.py", line 410, in _element_constructor_ return self.element_class(self, x) File "sage/rings/finite_rings/element_pari_ffelt.pyx", line 156, in sage.rings.finite_rings.element_pari_ffelt.FiniteFieldElement_pari_ffelt.__init__ (/projects/sage/sage-7.5/src/build/cythonized/sage/rings/finite_rings/element_pari_ffelt.c:3178) self.construct_from(x) File "sage/rings/finite_rings/element_pari_ffelt.pyx", line 325, in sage.rings.finite_rings.element_pari_ffelt.FiniteFieldElement_pari_ffelt.construct_from (/projects/sage/sage-7.5/src/build/cythonized/sage/rings/finite_rings/element_pari_ffelt.c:5456) self.construct_from(self._parent.polynomial_ring()(x)) File "sage/structure/parent.pyx", line 955, in sage.structure.parent.Parent.__call__ (/projects/sage/sage-7.5/src/build/cythonized/sage/structure/parent.c:9852) return mor._call_(x) File "sage/structure/coerce_maps.pyx", line 110, in sage.structure.coerce_maps.DefaultConvertMap_unique._call_ (/projects/sage/sage-7.5/src/build/cythonized/sage/structure/coerce_maps.c:4883) raise File "sage/structure/coerce_maps.pyx", line 105, in sage.structure.coerce_maps.DefaultConvertMap_unique._call_ (/projects/sage/sage-7.5/src/build/cythonized/sage/structure/coerce_maps.c:4750) return C._element_constructor(x) File "/projects/sage/sage-7.5/local/lib/python2.7/site-packages/sage/rings/polynomial/polynomial_ring.py", line 438, in _element_constructor_ return self(p.parse(x)) File "sage/misc/parser.pyx", line 494, in sage.misc.parser.Parser.parse (/projects/sage/sage-7.5/src/build/cythonized/sage/misc/parser.c:4633) cpdef parse(self, s, bint accept_eqn=True): File "sage/misc/parser.pyx", line 510, in sage.misc.parser.Parser.parse (/projects/sage/sage-7.5/src/build/cythonized/sage/misc/parser.c:4476) expr = self.p_eqn(tokens) if accept_eqn else self.p_expr(tokens) File "sage/misc/parser.pyx", line 712, in sage.misc.parser.Parser.p_eqn (/projects/sage/sage-7.5/src/build/cythonized/sage/misc/parser.c:6789) lhs = self.p_expr(tokens) File "sage/misc/parser.pyx", line 752, in sage.misc.parser.Parser.p_expr (/projects/sage/sage-7.5/src/build/cythonized/sage/misc/parser.c:7187) operand1 = self.p_term(tokens) File "sage/misc/parser.pyx", line 786, in sage.misc.parser.Parser.p_term (/projects/sage/sage-7.5/src/build/cythonized/sage/misc/parser.c:7467) operand1 = self.p_factor(tokens) File "sage/misc/parser.pyx", line 829, in sage.misc.parser.Parser.p_factor (/projects/sage/sage-7.5/src/build/cythonized/sage/misc/parser.c:7922) return self.p_power(tokens) File "sage/misc/parser.pyx", line 857, in sage.misc.parser.Parser.p_power (/projects/sage/sage-7.5/src/build/cythonized/sage/misc/parser.c:8085) operand1 = self.p_atom(tokens) File "sage/misc/parser.pyx", line 919, in sage.misc.parser.Parser.p_atom (/projects/sage/sage-7.5/src/build/cythonized/sage/misc/parser.c:8920) return self.variable_constructor(name) File "sage/misc/parser.pyx", line 1045, in sage.misc.parser.LookupNameMaker.__call__ (/projects/sage/sage-7.5/src/build/cythonized/sage/misc/parser.c:10179) return self.fallback(name) File "sage/structure/parent.pyx", line 955, in sage.structure.parent.Parent.__call__ (/projects/sage/sage-7.5/src/build/cythonized/sage/structure/parent.c:9852) return mor._call_(x) File "sage/structure/coerce_maps.pyx", line 110, in sage.structure.coerce_maps.DefaultConvertMap_unique._call_ (/projects/sage/sage-7.5/src/build/cythonized/sage/structure/coerce_maps.c:4883) raise File "sage/structure/coerce_maps.pyx", line 105, in sage.structure.coerce_maps.DefaultConvertMap_unique._call_ (/projects/sage/sage-7.5/src/build/cythonized/sage/structure/coerce_maps.c:4750) return C._element_constructor(x) File "/projects/sage/sage-7.5/local/lib/python2.7/site-packages/sage/rings/finite_rings/integer_mod_ring.py", line 1158, in _element_constructor_ return integer_mod.IntegerMod(self, x) File "sage/rings/finite_rings/integer_mod.pyx", line 179, in sage.rings.finite_rings.integer_mod.IntegerMod (/projects/sage/sage-7.5/src/build/cythonized/sage/rings/finite_rings/integer_mod.c:4407) return IntegerMod_int(parent, value) File "sage/rings/finite_rings/integer_mod.pyx", line 2230, in sage.rings.finite_rings.integer_mod.IntegerMod_int.__init__ (/projects/sage/sage-7.5/src/build/cythonized/sage/rings/finite_rings/integer_mod.c:26497) z = sage.rings.integer_ring.Z(value) File "sage/structure/parent.pyx", line 955, in sage.structure.parent.Parent.__call__ (/projects/sage/sage-7.5/src/build/cythonized/sage/structure/parent.c:9852) return mor._call_(x) File "sage/structure/coerce_maps.pyx", line 110, in sage.structure.coerce_maps.DefaultConvertMap_unique._call_ (/projects/sage/sage-7.5/src/build/cythonized/sage/structure/coerce_maps.c:4883) raise File "sage/structure/coerce_maps.pyx", line 105, in sage.structure.coerce_maps.DefaultConvertMap_unique._call_ (/projects/sage/sage-7.5/src/build/cythonized/sage/structure/coerce_maps.c:4744) return C._element_constructor(x) File "sage/rings/integer.pyx", line 680, in sage.rings.integer.Integer.__init__ (/projects/sage/sage-7.5/src/build/cythonized/sage/rings/integer.c:5999) mpz_set_str_python(self.value, x, base) File "sage/rings/integer.pyx", line 6697, in sage.rings.integer.mpz_set_str_python (/projects/sage/sage-7.5/src/build/cythonized/sage/rings/integer.c:42343) raise TypeError("unable to convert %r to an integer" % s) TypeError: unable to convert 'y' to an integer

Několik pokusných algebraických operací.

print "Soucet" a+b print "Soucin" a*b print "Sta mocnina prvku $a$" a^100 print "Inverzni prvek k $a$" c = a^-1 c print "Test inverze" c*a
Soucet 872*x^2 + 42*x + 1 Soucin 871*x^4 + 618*x^3 + 246*x^2 + 7*x Sta mocnina prvku $a$ 750*x^4 + 247*x^3 + 523*x^2 + 616*x + 202 Inverzni prvek k $a$ 203*x^4 + 403*x^3 + 681*x^2 + 773*x + 207 Test inverze 1

Na algebraických operacích není tedy nic překvapivého. SageMath nám ale umožňuje zjistit zajímavé informace o tělese TT samotném. Už jsme si ukázali, jak zjistit modul, vůči kterému počítáme. Dále máme k dispozici následující vlastnosti tělesa TT. Začněme několika základními.

print "Rad telesa (pocet prvku)" T.order() print "T je skutecne teleso" T.is_field() print "Modul" T.modulus() print "Charakteristika $T$" T.characteristic()
Rad telesa (pocet prvku) 655507336820599 T je skutecne teleso True Modul x^5 + 11*x + 912 Charakteristika $T$ 919

Na každé těleso se můžeme dívat jako na rozšíření jeho prvotělesa.

print "Prvoteleso naseho $T$" T.prime_subfield() # prvopodteleso telesa T T.base_ring() # T je nyni chapano jako rozsireni sveho prvotelesa (default), metoda base_ring je obecnejsi metoda, ktera nam vraci zakladni okruh, nad kterym zrovna bydlime print "Pripomenme, ze hodnota naseho $p$ je..." p print "Stupen $T$ jakozto rozsireni nad $GF(p)$" T.degree() print "Pripomenme, ze hodnota naseho $n$ je..." n
Prvoteleso naseho $T$ Finite Field of size 919 Finite Field of size 919 Pripomenme, ze hodnota naseho $p$ je... 919 Stupen $T$ jakozto rozsireni nad $GF(p)$ 5 Pripomenme, ze hodnota naseho $n$ je... 5

O multiplikativní grupě tělesa TT, T=(T{0},)T^* = (T\smallsetminus\{0\},\cdot) víme, že je cyklická. Sage nám umožňuje hledat její generátory (tzv. primitivní prvky).

a = T.primitive_element() # Odpoved SageMath by v tomto pripada nemela byt prekvapiva! a
x

Ověřme, že a=xa = x je skutečně generátorem. Stačí (a v našem případě to stále ještě lze) ověřit, že negeneruje netriviální podgrupu. (Ověření provedeme poměrně brutální metodou...)

multi_order = T.order()-1 # rad multiplikativni grupy divs = divisors(multi_order) # delitele radu print "Delitele radu multiplikativni grupy:" divs for k in divs: a^k # mocniny prvku $a$ na delitel radu. del(multi_order,divs)
Delitele radu multiplikativni grupy: [1, 2, 3, 6, 9, 17, 18, 27, 34, 51, 54, 102, 153, 306, 459, 918, 714060279761, 1428120559522, 2142180839283, 4284361678566, 6426542517849, 12139024755937, 12853085035698, 19279627553547, 24278049511874, 36417074267811, 38559255107094, 72834148535622, 109251222803433, 218502445606866, 327753668410299, 655507336820598] x x^2 x^3 908*x^2 + 7*x 7*x^4 + 121*x + 842 703*x^4 + 221*x^3 + 343*x^2 + 856*x + 792 221*x^4 + 343*x^3 + 856*x^2 + 411*x + 326 561*x^4 + 10*x^3 + 882*x^2 + 564*x + 262 889*x^4 + 22*x^3 + 279*x^2 + 335*x + 582 896*x^4 + 386*x^3 + 281*x^2 + 528*x + 860 528*x^4 + 194*x^3 + 188*x^2 + 530*x + 129 78*x^4 + 816*x^3 + 307*x^2 + 724*x + 140 690*x^4 + 712*x^3 + 453*x^2 + 614*x + 37 516*x^4 + 280*x^3 + 294*x^2 + 241*x + 2 325*x^4 + 890*x^3 + 565*x^2 + 707*x + 488 567*x^4 + 639*x^3 + 452*x^2 + 215*x + 833 7 49 343 17 317 308 318 635 207 345 703 474 867 866 918 1

Vidíme, že výsledek 11 dostáváme pouze pro poslední prvek seznamu (což je řád multiplikativní grupy). Pro netriviální dělitele 11 nezískáváme a aa proto negeneruje grupu menšího řádu.

%md Hledání primitivního prvku je zajímavější úloha. Podívejme se, jak ji SageMath vykonává.

Hledání primitivního prvku je zajímavější úloha. Podívejme se, jak ji SageMath vykonává.

T.multiplicative_generator??
File: /projects/sage/sage-7.5/src/sage/rings/finite_rings/finite_field_base.pyx Source: def multiplicative_generator(self): """ Return a primitive element of this finite field, i.e. a generator of the multiplicative group. You can use :meth:`multiplicative_generator()` or :meth:`primitive_element()`, these mean the same thing. .. WARNING:: This generator might change from one version of Sage to another. EXAMPLES:: sage: k = GF(997) sage: k.multiplicative_generator() 7 sage: k.<a> = GF(11^3) sage: k.primitive_element() a sage: k.<b> = GF(19^32) sage: k.multiplicative_generator() b + 4 TESTS: Check that large characteristics work (:trac:`11946`):: sage: p = 10^20 + 39 sage: x = polygen(GF(p)) sage: K.<a> = GF(p^2, modulus=x^2+1) sage: K.multiplicative_generator() a + 12 """ from sage.arith.all import primitive_root if self.__multiplicative_generator is not None: return self.__multiplicative_generator if self.degree() == 1: self.__multiplicative_generator = self(primitive_root(self.order())) return self.__multiplicative_generator n = self.order() - 1 g = self.gen(0) # We check whether x+g is a multiplicative generator, where # x runs through the finite field. # This has the advantage that g is the first element we try, # so we always get g as generator if possible. Second, the # PARI finite field iterator gives all the constant elements # first, so we try g+(constant) before anything else. for x in self: a = g+x if a != 0 and a.multiplicative_order() == n: self.__multiplicative_generator = a return a

Metoda gen nevrací generátor ve smyslu generátoru cyklické grupy, ale generátor ve smyslu matematického objektu SageMath. V našem případě jsem výše tento prvek označili symbolem xx. V této konstrukci jde obecně přímo o formální proměnnou polynomů, se kterými pracujeme.

Z posledních několika řádků metody multiplicative_generator je vidět, že hledání primitivního prvku v SageMath není nijak sofistikované, naopak... Tedy je prostor pro zlepšení.

Druhou variantou je použít konstruktor konečného tělesa takto GF(5^40, modulus="primitive"). Bude nám vybrán polynom, jehož kořen bude primitivním prvkem a nebude co počítat.

Na závěr znovu poznamenejme, jak lze vytvořit konečné těleso řádu pnp^n, s vlastním ireducibilním polynomem. Existují dvě možnosti. První možností je přímo použít faktorizační proceduru:

p = 587 # budouci charakteristika R.<y> = PolynomialRing(GF(p)) # $R$ je okruh vsech polynomu nad $GF(p)$ v promenne $y$ f = y^7 + 585*y^5 + y^2 + y + 3 # zvoleny ireducibilni polynom f.is_irreducible() # $f$ skutecne je ireducibilni nad $GF(p)$ I = R.principal_ideal(f) # hlavni ideal okruhu $R$ generovany prvekem $f$ T1 = QuotientRing(R,I) # Faktorokruh $T$ vzhledem k $I$, ocekavane teleso T1
True Univariate Quotient Polynomial Ring in ybar over Finite Field of size 587 with modulus y^7 + 585*y^5 + y^2 + y + 3

Druhou možností je opět použití příkazu GF.

T2 = GF(587^7, name='y', modulus=f) T2 T2.modulus()
Finite Field in y of size 587^7 x^7 + 585*x^5 + x^2 + x + 3

Rozšíření těles

Konstrukce rozšíření

Z přednášky víme, že každé těleso TT lze chápat jako vektorový prostor nad libovolným jeho podtělesem KK. Těleso KK pak také nazýváme rozšířením tělesa KK. V této sekci si ukážeme některé pojmy z přednášky na konkrétním příkladě T=GF(28)T = GF(2^8).

T = GF(2^8,name='x')

Sage automaticky chápe GF(28)GF(2^8) jako jednoduché rozšíření jeho prvotělesa GF(2)GF(2).

T.base_ring() T.degree()
Finite Field of size 2 8

Z přednášky víme, že GF(2k)GF(2^k) je podtělesem GF(28)GF(2^8), právě když kk je dělitel čísla 8. GF(28)GF(2^8) má proto pouze podtělesa GF(2k)GF(2^k) pro následující kk (zahrnujeme i triviální podtělesa).

divisors(T.degree())
[1, 2, 4, 8]
subs = T.subfields() subs
[(Finite Field of size 2, Conversion map: From: Finite Field of size 2 To: Finite Field in x of size 2^8), (Finite Field in x2 of size 2^2, Ring morphism: From: Finite Field in x2 of size 2^2 To: Finite Field in x of size 2^8 Defn: x2 |--> x^7 + x^6 + x^4 + x^2 + x), (Finite Field in x4 of size 2^4, Ring morphism: From: Finite Field in x4 of size 2^4 To: Finite Field in x of size 2^8 Defn: x4 |--> x^7 + x^4 + x^3), (Finite Field in x8 of size 2^8, Ring morphism: From: Finite Field in x8 of size 2^8 To: Finite Field in x of size 2^8 Defn: x8 |--> x)]

Uvažme TT jakožto rozšíření K=GF(24)K = GF(2^4).

K.<y> = GF(2^4) K
Finite Field in y of size 2^4

Buď RR okruh polynomů v proměnné zz nad KK.

R.<z> = PolynomialRing(K)

Zvolme ireducibilní polynom z RR.

g = z^2+y*z+1 g.is_irreducible()
True

Hledané rozšíření tělesa KK pak získáme faktorizací okruhu RR vůči hlavnímu ideálu generovanému gg.

I = R.principal_ideal(g) extK = QuotientRing(R,I) print 'rad rozsireni' extK.order() extK.base_ring()
rad rozsireni 256 Finite Field in y of size 2^4

Polynomiální a normální báze

V této části si ukážeme, jak lze na využít struktury vektorového prostoru v tělesech. Nejprve se budeme věnovat polynomiálním a poté normálním bázím.

Polynomiální báze

Jako jednoduchý příklad uvažme těleso T=GF(28)T = GF(2^8) chápané jako vektorový prostor nad GF(2)GF(2).

Sestrojení báze

T.<x> = GF(2^8) T T.base_ring()
Finite Field in x of size 2^8 Finite Field of size 2

Příslušný vektorový prostor označme symbolem VV. VV je tedy vektorový prostor TT nad GF(2)GF(2).

V = T.vector_space() V
Vector space of dimension 8 over Finite Field of size 2

Báze tvořená prvky (1,x,,x7)(1,x,\ldots,x^7) se v tomto případě nazývá polynomiální bází.

polyB = [ T(v) for v in V.basis() ] # V.basis() vraci standardni bazi V polyB
[1, x, x^2, x^3, x^4, x^5, x^6, x^7]

Uvažme dva testovací prvky našeho tělesa TT.

f = x^2 + 1 g = x^6 + x^3 + x

Jejich souřadnice vzhledem k polynomiální bázi lze snadno získat pomocí příkazu _vector_.

vecf = f._vector_(reverse=True) vecg = g._vector_() print 'Souradnice $f$' vecf print 'Souradnice $g$' vecg
Souradnice $f$ (0, 0, 0, 0, 0, 1, 0, 1) Souradnice $g$ (0, 1, 0, 1, 0, 0, 1, 0)

Sčítání

Operace sčítání vektorů po složkách (v každé složce modulo 2) odpovídá sčítání v TT. Přesvědčíme se o tom například v následujícím pokusu.

print 'Soucet polynomu $f+g$' f + g print 'Soucet prislusnych vektoru' vecf + vecg print 'Soucet prislusnych vektoru prevedeny zpet do polynomialni reprezentace' T(vecf + vecg)
Soucet polynomu $f+g$ x^6 + x^3 + x^2 + x + 1 Soucet prislusnych vektoru (0, 1, 0, 1, 0, 1, 1, 1) Soucet prislusnych vektoru prevedeny zpet do polynomialni reprezentace x^7 + x^6 + x^5 + x^3 + x

Násobení

Násobení je komplikovanější. V obecném vektorovém prostoru neexistuje kanonický způsob násobení dvou vektorů s vektorovým výstupem (známý vektorový součin funguje pouze v R3\mathbb{R}^3).

f * g
x^6 + x^5 + x^4 + x^3 + x^2 + x + 1

Náš vektorový prostor je ale zároveň tělesem. Pro fixní ff lze zobrazení gfgg \mapsto f*g chápat jako lineární zobrazení! Sestrojme jeho matici v naší polynomiální bázi. SageMath má pro tento účel dokonce rovnou zabudovanou funkci. Pro další referenci si tuto matici označme MfM_f.

matf = f._matrix_() print 'Matice zobrazeni nasobeni vektorem $f$' show(matf)
Matice zobrazeni nasobeni vektorem $f$
(1000001001000001101000100101001100101011000101010000101000000101)\left(\begin{array}{rrrrrrrr} 1 & 0 & 0 & 0 & 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 & 0 & 0 & 0 & 1 \\ 1 & 0 & 1 & 0 & 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 1 & 0 & 0 & 1 & 1 \\ 0 & 0 & 1 & 0 & 1 & 0 & 1 & 1 \\ 0 & 0 & 0 & 1 & 0 & 1 & 0 & 1 \\ 0 & 0 & 0 & 0 & 1 & 0 & 1 & 0 \\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 1 \end{array}\right)

Součinu fgf\cdot g v TT pak odpovídá vektor MfgM_f g, kde pod gg máme na mysli vektorovou reprezentaci gg.

print 'Soucin jako vektor' matf * vecg print 'Soucin jako polynom' T(matf * vecg) print 'Soucin v telese' f*g
Soucin jako vektor (1, 1, 1, 1, 1, 1, 1, 0) Soucin jako polynom x^6 + x^5 + x^4 + x^3 + x^2 + x + 1 Soucin v telese x^6 + x^5 + x^4 + x^3 + x^2 + x + 1

Připomeňme, jak lze matici lineárního zobrazení sestrojit (bez odvolání se na příkaz _matrix_). Sloupce matice MfM_f představují součiny ff a vektorů polynomiální báze v polynomiální bázi. Přesněji:

mymatf = matrix(T.base_ring(),[ (f * b)._vector_() for b in polyB ]).transpose() # matice s koeficienty z GF(2) mymatf == matf
True

Alternativní metodou je vyjádřit násobení jako bilineární zobrazení V×VVV \times V \to V. Je-li B=(b0,b1,,b7)\mathcal{B} = (b_0,b_1,\ldots,b_7) standardní báze, pak pro součin dvou vektorů ff a gg platí fg=i=07j=07fifj(bibj), f \cdot g = \sum_{i=0}^7 \sum_{j=0}^7 f_i f_j (b_i \cdot b_j), kde (fi)(f_i) a (fj)(f_j) jsou souřadnice ff a gg vzhledem k BB a tečka označuje násobení v TT.

bilinear = matrix(T, T.degree(), T.degree(), lambda i,j: x^i * x^j) # 'matice' bilinearni formy bilinear
[ 1 x x^2 x^3 x^4 x^5 x^6 x^7] [ x x^2 x^3 x^4 x^5 x^6 x^7 x^4 + x^3 + x^2 + 1] [ x^2 x^3 x^4 x^5 x^6 x^7 x^4 + x^3 + x^2 + 1 x^5 + x^4 + x^3 + x] [ x^3 x^4 x^5 x^6 x^7 x^4 + x^3 + x^2 + 1 x^5 + x^4 + x^3 + x x^6 + x^5 + x^4 + x^2] [ x^4 x^5 x^6 x^7 x^4 + x^3 + x^2 + 1 x^5 + x^4 + x^3 + x x^6 + x^5 + x^4 + x^2 x^7 + x^6 + x^5 + x^3] [ x^5 x^6 x^7 x^4 + x^3 + x^2 + 1 x^5 + x^4 + x^3 + x x^6 + x^5 + x^4 + x^2 x^7 + x^6 + x^5 + x^3 x^7 + x^6 + x^3 + x^2 + 1] [ x^6 x^7 x^4 + x^3 + x^2 + 1 x^5 + x^4 + x^3 + x x^6 + x^5 + x^4 + x^2 x^7 + x^6 + x^5 + x^3 x^7 + x^6 + x^3 + x^2 + 1 x^7 + x^2 + x + 1] [ x^7 x^4 + x^3 + x^2 + 1 x^5 + x^4 + x^3 + x x^6 + x^5 + x^4 + x^2 x^7 + x^6 + x^5 + x^3 x^7 + x^6 + x^3 + x^2 + 1 x^7 + x^2 + x + 1 x^4 + x + 1]

Násobení pak provedeme “obložením” takto jednou napočtené matice a souřadnicemi příslušných vektorů. Protože jsme v matici nechali rovnou výrazy z TT, dostaneme rovnou polynomiální výsledek.

print 'nasobeni pomoci matice' vecf * bilinear * vecg print 'nasobeni v $T$' f*g
nasobeni pomoci matice x^6 + x^4 + x^3 + x + 1 nasobeni v $T$ x^6 + x^5 + x^4 + x^3 + x^2 + x + 1

Normální báze

Ve vektorovém prostoru máme nepřeberné množství různých bází. Pro různé operace může být výhodnější používat vhodně zvolenou bázi. V normálních bázích se například snadno mocní. Opět budeme pracovat v T=GF(28)T = GF(2^8) chápaném jako vektorový prosto VV nad GF(2)GF(2).

T.<x> = GF(2^8) T V = T.vector_space() V
Finite Field in x of size 2^8 Vector space of dimension 8 over Finite Field of size 2

Zvolme nyní následující prvek tělesa TT.

a = x^7 + x^5 + x^2 + 1

Normální báze je báze tvaru (a,a2,a22,,a27)(a,a^{2},a^{2^2},\ldots,a^{2^7}). Je nutné ověřit, že tento soubor je lineárně nezávislý a tvoří skutečně bázi! To není automaticky splněno (zvolte např. a=1a=1).

normalB = [ a^(2^i) for i in range(T.degree()) ] print 'soubor vektoru:' normalB print 'jeho delka:' len(normalB)
soubor vektoru: [x^7 + x^5 + x^2 + 1, x^6 + x^5 + x^4 + x^2 + x, x^7 + x^5 + x^4, x^6 + x^5 + x^4 + x^3 + x, x^7 + x^6 + x^5, x^7 + x^5 + x^3 + x, x^5 + x + 1, x^6 + x^5 + x^4 + 1] jeho delka: 8

Otestujme lineární nezávislost (případně máme i kritérium z přednášky).

W = [ b._vector_() for b in normalB ] # soubor vektoru z V W
[(1, 0, 1, 0, 0, 1, 0, 1), (0, 1, 1, 0, 1, 1, 1, 0), (0, 0, 0, 0, 1, 1, 0, 1), (0, 1, 0, 1, 1, 1, 1, 0), (0, 0, 0, 0, 0, 1, 1, 1), (0, 1, 0, 1, 0, 1, 0, 1), (1, 1, 0, 0, 0, 1, 0, 0), (1, 0, 0, 0, 1, 1, 1, 0)]
V.span(W).dimension() # dimenze linearniho obalu W (vzhledem k strukture V)
8

Námi zvolený soubor proto skutečně tvoří bázi. Pro snadné počítání se souřadnicemi vzhledem k této bázi nám Sage poskytuje následující metody.

def back(coords, basis): """ Vrati lineerni kombinaci vektoru z basis s koeficienty z coords. """ return sum( [ coords[i] * basis[i] for i in range(len(basis)) ] )
normalV = V.span_of_basis(W) # V s nami zvolenou normalni bazi print 'Testovaci prvek' f = x^6 + x^4 + x f print 'Souradnice v normalni bazi' coordsf = normalV.coordinates(f) # jeho souradnice vzhledem k normalni bazi coordsf print 'a zpet' back(coordsf, normalB)
Testovaci prvek x^6 + x^4 + x Souradnice v normalni bazi [0, 0, 0, 0, 0, 0, 1, 1] a zpet x^6 + x^4 + x

Z přednášky víme, že mocnění lze v normální bázi realizovat jako pouhé cyklické posunutí souřadnic. Ověřme si to na našem příkladě. Vypočtěme souřadnice mocnin f2if^{2^i}.

for i in range(T.degree()): print '=======' print 'i =', i print 'souradnice v normalni bazi' print normalV.coordinates(f^(2^i)) print 'samotna mocnina' print f^(2^i)
======= i = 0 souradnice v normalni bazi [0, 0, 0, 0, 0, 0, 1, 1] samotna mocnina x^6 + x^4 + x ======= i = 1 souradnice v normalni bazi [1, 0, 0, 0, 0, 0, 0, 1] samotna mocnina x^7 + x^6 + x^4 + x^2 ======= i = 2 souradnice v normalni bazi [1, 1, 0, 0, 0, 0, 0, 0] samotna mocnina x^7 + x^6 + x^4 + x + 1 ======= i = 3 souradnice v normalni bazi [0, 1, 1, 0, 0, 0, 0, 0] samotna mocnina x^7 + x^6 + x^2 + x ======= i = 4 souradnice v normalni bazi [0, 0, 1, 1, 0, 0, 0, 0] samotna mocnina x^7 + x^6 + x^3 + x ======= i = 5 souradnice v normalni bazi [0, 0, 0, 1, 1, 0, 0, 0] samotna mocnina x^7 + x^4 + x^3 + x ======= i = 6 souradnice v normalni bazi [0, 0, 0, 0, 1, 1, 0, 0] samotna mocnina x^6 + x^3 + x ======= i = 7 souradnice v normalni bazi [0, 0, 0, 0, 0, 1, 1, 0] samotna mocnina x^7 + x^3 + 1

Na závěr poznamenejme, že operace sčítání opět působí jednoduše po složkách. Operace obyčejného násobení v normální bázi nemá žádné zvláštní vlastnosti. Mocnění má výše pozorovnanou vlastnost pouze, pokud mocníme na řád příslušného podtělesa.

Ukázka práce nad větším podtělesem

Uvažme opět T=GF(28)T = GF(2^8) a chápejme ho jako rozšíření tělesa GF(24)GF(2^4). Tento příklad jsme uvažovali již v předcházejícím textu, proto pouze rychle vytvoříme potřebné objekty.

K.<x> = GF(2^4) R.<y> = PolynomialRing(K) g = y^2 + x*y + 1 T = K.extension(g) T.order() T
256 Univariate Quotient Polynomial Ring in y over Finite Field in x of size 2^4 with modulus y^2 + x*y + 1

Vektorový prostor nad KK dimenze 22, tj. stupně definujícího prvku gg.

V = VectorSpace(K,g.degree()) V
Vector space of dimension 2 over Finite Field in x of size 2^4

Polynomiální báze.

polyB = [1,y]

Testovací element.

f = x^3*y+x

Nyní je potřeba dvou pomocných funkcí.

def toV(poly): """ Nalezne souradnice poly ve standardni (tj. zde polynomialni) bazi V """ return V(poly.polynomial(y).padded_list()) def back(coords,basis): """ Vrati linearni kombinaci vektoru z basis s koeficienty coords (mysleno v $T$) """ return T(sum( [ coords[i] * basis[i] for i in range(len(basis)) ] ))
print 'testovaci $f$' f print 'souradnice $f$' toV(f) print 'vyjadreni opet v $T$' print back(toV(f),polyB)
testovaci $f$ x^3*y + x souradnice $f$ (x, x^3) vyjadreni opet v $T$ x^3*y + x

Podívejme se opět na operaci násobení v TT, pracujeme v polynomiální bázi.

bilinear = matrix(T, T.degree(), T.degree(), lambda i,j: y^i * y^j) # 'matice' bilinearni formy show(bilinear)
(1yyxy+1)\left(\begin{array}{rr} 1 & y \\ y & x y + 1 \end{array}\right)
veca = V([K.random_element(),K.random_element()]) vecb = V([K.random_element(),K.random_element()]) print 'testovaci vektor $a$' veca print 'testovaci vektor $b$' vecb
testovaci vektor $a$ (1, 0) testovaci vektor $b$ (x + 1, x^3 + x + 1)

Součin pomocí maticového násobení a ověření výpočtem v TT.

print 'Dle matice' c1 = veca * bilinear * vecb c1 print 'Dle operaci v $T$' c2 = back(veca,polyB) * back(vecb,polyB) c2 print 'Test rovnosti' c1 == c2
Dle matice (x^3 + x + 1)*y + x + 1 Dle operaci v $T$ (x^3 + x + 1)*y + x + 1 Test rovnosti True