Diszkrét matematika 10. előadás Sapientia Egyetem, Matematika-Informatika Tanszék Marosvásárhely, Románia mgyongyi@ms.sapientia.ro 2017, őszi félév
Miről volt szó az elmúlt előadáson? a prímszámtétel prímszámok, tulajdonságok, sejtések hatványok és generátor elemek a diszkrét logaritmus (DL) probléma a Diffie-Hellman kulcscsere az eukleidészi algoritmus és változatai lineáris kongruenciák moduláris inverz
Miről lesz szó? moduláris inverz, algoritmusok a brute-force algoritmus a kiterjesztett Eukleidészi algoritmus a kis Fermat tételen alapuló algoritmus a diszkrét logaritmus, algoritmusok a brute-force algoritmus Baby-step Giant-step, Shanks algoritmusa a számelmélet alaptétele összetett számok faktorizációja: a Fermat féle faktorizáció
A moduláris inverz 1. értelmezés Az a x 1 (mod m) kongruenciának a megoldását, ahol lnko(a, m) = 1 az a szám moduláris inverzének hívjuk (mod m) szerint. Megjegyzés a fenti egyenlet egy sajátos esete az a x b (mod m) egyenletnek: b = 1. A moduláris inverz meghatározása, módszerek: kipróbáljuk az összes lehetséges értéket (brute force, BF) alkalmazzuk a kiterjesztett Eukleidészi algoritmust ha a modulus prímszám, akkor a kis Fermat tétel segítségével, másképp az Euler tétel segítségével a kiterjesztett Eukleidészi algoritmus, illetve a kis Fermat tételén alapuló algoritmus logaritmikus idejű, azaz hatékony algoritmusok
A moduláris inverz 1. feladat Határozzuk meg a moduláris inverz értékét brute force illetve a kis Fermat tétel segítségével. def inverzbf(a, m): for i in range(1, m): if (a*i) % m == 1: return i def inverzfermat(a, m): res = pow(a, m-2, m) if (res * a) % m!= 1: print m, nem primszam! return -1 return res
A moduláris inverz 2. feladat Hasonĺıtsuk össze a három moduláris inverzet számoló algoritmus futási idejét def main1(a, m): st = time.time() res = inverzbf(a, m) fs = time.time() print fs - st print inverz:, res st = time.time() res = inverzfermat(a, m) fs = time.time() print fs - st print inverz:, res >>> main1(2, 4221109) 0.440999984741 inverz: 2110555 0.0 inverz: 2110555 0.0 inverz: 2110555 st = time.time() res = inverz(a, m) #a 9. eloadason megadott inverz fuggveny fs = time.time() print fs - st print inverz:, res
A moduláris inverz 3. feladat Hasonĺıtsuk össze az Eukleidészi algoritmuson alapuló, illetve a kis Fermat tételén alapuló moduláris inverzet számoló algoritmusok futási idejét, egy m 1024 bites prímszám esetében def main2(): a = 2 m = 1204328148337291603642924232769208462720078 78689251114536602300016710024726076936509435386 49083953166853517249346367715669274167793296290 86878213191274539205544604728549441021320463964 58538345025572719153741961623463125980738349016 07120494461215543243875002652798979907105247593 5039352681744845614576741318513 st = time.time() res = inverzfermat(a, m) fs = time.time() print fs - st print inverz:, res st = time.time() res = inverz(a, m) fs = time.time() print fs - st print inverz:, res >>> main2() 0.0490000247955 inverz: 60216407416864580182146211638460423 13600393934462555726830115000835501 23630384682547176932454197658342675 86246731838578346370838966481454343 91065956372696027723023642747205106 60231982292691725127863595768709808 11731562990369174508035602472306077 71621937501326399489953552623796751 9676340872422807288370659257 0.0 inverz: 60216407416864580182146211638460423... 967519676340872422807288370659257
A diszkrét logaritmus (DL) probléma, algoritmusok Az egész számok Z p = {1, 2,..., p 1} véges halmaza esetében, ahol p prímszám a DL probléma a következő: az A, g-alapú diszkrét logaritmusa (mod p) szerint azt jelenti, hogy megkeressük azt az a pozitív egész számot, melyre fennáll: g a A (mod p), ahol g, A Z p. Nagy számok esetében a DL probléma meghatározására nem ismert hatékony algoritmus. 4. feladat Határozzuk meg a diszkrét logaritmus értékét a legkézenfekvőbb módon (brute-force): meghatározzuk az g 0, g 1, g 2,... értékeket, mindaddig, amíg az nem kongruens A-val: def dlog(p, g, A): for i in range(1, p-1): if pow(g, i, p) == A: return i
Baby-step Giant-step, Shanks algoritmusa Nem triviális algoritmusok a DL problémára: a Shank (baby-step giant-step) algoritmus, a Pohlig-Hellman algoritmus, az indexkalkulus algoritmus. A baby-step giant-step algoritmus alapötlete Daniel Shanks-től származik 1971-ből. a BsGs algoritmus a brute force algoritmus egy módosított változata.
Baby-step Giant-step, Shanks algoritmusa ha a kitevő értékét átírjuk: a = i m + j, ahol 0 i, j < m és m = p 1, akkor Az algoritmus: g a = g i m+j = g i m g j g j = g a g m i = A (g 1 ) m i. meghatározzuk a g j értékeket, j = 0, 1,..., m 1 meghatározzuk g inverzét (mod p) szerint: ginv = g 1 -t meghatározzuk ginvm = ginv m értékét megvizsgáljuk, hogy milyen i = 0, 1,..., m 1 -re egyezik meg A (ginvm i ) értéke valamely g j -vel.
Baby-step Giant-step, Shanks algoritmusa Példa: Határozzuk meg a diszkrét logaritmus értékét p = 19, g = 2, A = 10 esetében. a kitevő értékét átírjuk: a = i m + j, ahol m = 18 = 5 meghatározzuk a g j értékeket, j = 1, 2, 3, 4-re, azaz g 0 = 1, g 1 = 2, g 2 = 4, g 3 = 9, g 4 = 16. meghatározzuk g = 2 inverzét: ginv = 10, fennáll: 2 10 = 1 (mod 19) meghatározzuk ginvm = ginv m = 10 5 = 3 (mod 19) értéket megvizsgáljuk, hogy melyik i = 0, 1, 2, 3, 4 -re egyezik meg A (ginvm i ) értéke valamely g j -vel, azaz: 10 3 0 = 10, 10 3 1 = 11, 10 3 2 = 14, 10 3 3 = 4 i = 3, j = 2 esetén van találat, tehát a kitevő = 3 5 + 2 = 17
Baby-step Giant-step, Shanks algoritmusa 5. feladat Határozzuk meg a diszkrét logaritmus értékét Shanks algoritmusával. def dlog_1(p, g, A): m = int(math.ceil((p-1) ** 0.5)) L = [] for j in range(m): L = L + [pow(g, j, p)] ginv = inverz(g, p) ginvm = pow(ginv, m, p) for i in range(m): c = (A * pow(ginvm, i, p)) % p for j in range(len(l) - 1): if c == L[j]: return i * m + j return -1
Baby-step Giant-step, Shanks algoritmusa 6. feladat Optimalizáljuk Shanks algoritmusát: a hatványértékeket az előző értékek alapján számoljuk ki. def dlog_2(p, g, A): m = int(math.ceil((p-1) ** 0.5)) pp = 1 L = [1] for j in range(1, m): pp = (pp * g) % p L = L + [pp] ginv = inverz(g, p) ginvm = pow(ginv, m, p) pp = 1 for i in range(m): c = (A * pp) % p for j in range(0, len(l) - 1): if c == L[j]: return i * m + j pp = (pp * ginvm) % p return -1
A diszkrét logaritmus (DL) probléma, algoritmusok 7. feladat Hasonĺıtsuk össze a három Dlog algoritmus futási idejét def main3(p, g, A): st = time.time() res = dlog(p, g, A) fs = time.time() print fs - st print kitevo:, res st = time.time() res = dlog_1(p, g, A) fs = time.time() print fs - st print kitevo:, res print fs - st print kitevo:, res >>> main3(530063, 1234, 10000) 0.631000041962 kitevo: 73132 0.0289998054504 kitevo: 73132 0.0169999599457 kitevo: 73132 st = time.time() res = dlog_2(p, g, A) fs = time.time()
A számelmélet alaptétele 1. tétel Bármely 1-nél nagyobb, pozitív egész szám egyértelműen feĺırható prímszámok szorzataként, ahol eltekintünk a szorzótényezők sorrendjétől. Egy a szám prímtényezős felbontására a következő jelölést használjuk: Példák: a = p a 1 1 pa 2 2... pan n 2200 = 2 2 2 5 5 11 = 2 3 5 2 11 361 = 19 19 = 19 2 10127 = 13 19 41 = n A fenti alakot kanonikus alaknak nevezzük. A felbontási folyamatot prímfelbontásnak, vagy prímfaktorizációnak nevezzük. i=1 p a i i
Prímtényezőkre bontás, megjegyzések Nagy számok esetében, a prímtényezős felbontás meghatározására nem ismert hatékony eljárás. A számítástechnika, matematika területén különösen fontosak a prímfaktorizációs algoritmusok. Prímfaktorizációs módszerek: kipróbáljuk az oszthatóságot a lehetséges prímszámokkal (brute force) Fermat-féle faktorizáció a Pollard ρ faktorizáció...
Prímtényezőkre bontás, megjegyzések Az Euler φ függvény meghatározásához szükség van a prímtényezős felbontásra. Két szám prímtényezős felbontása alapján meghatározható, a két szám legnagyobb közös osztója, legkisebb közös többszöröse, nagy számok esetében ez a módszer azonban ez nem hatékony. Az a és b egész számok legkisebb közös többszöröse, az a legkisebb szám amely osztható a-val és b-vel is. Jelölése: lkkt(a, b), vagy [a, b]. Ha a = p a i i és b = p b i i, akkor lnko(a, b) = p min{a i,b i } i. Ha a = p a i i és b = p b i i, akkor lkkt(a, b) = p max{a i,b i } i. Kapcsolat a legnagyobb közös osztóval: (a, b) [a, b] = a b.
Fermat-féle faktorizáció Legyen n = p q. Ha a p és a q prímek közel vannak egymáshoz, akkor az n faktorizálása lehetséges a Fermat-féle faktorizációs módszerrel: ha sikerül feĺırni n-et a következőképpen: n = a 2 b 2, ahol a, b tetszőleges egész számok, akkor p = a b, q = a + b. Határozzuk meg n = 6283 két prímosztóját, a Fermat-féle faktorizációs módszerrel: n a b = a 2 n négyzetszám-e? 80 80 117 nem 81 278 nem 82 441 igen 441 = 21 p = 82 21 = 61, q = 82 + 21 = 103
Fermat-féle faktorizáció 8. feladat Határozzuk meg egy összetett szám prímtényezős felbontását a Fermat-féle faktorizációs algoritmussal. import math def fermat (n): getcontext().prec = 400 a = long(decimal(n).sqrt()) + 1 while True: b = a * a - n b1 = negyzetsz(b) if b1 <> -1: return (a - b1, a + b1) a += 1
Fermat-féle faktorizáció def negyzetsz(x): getcontext().prec = 400 i = long(decimal(x).sqrt()) if i*i == x: return i else: return -1 def negyzetsz1(x): i = 2 while i * i <= x: if x == i*i: return i i += 1 return -1
Fermat-féle faktorizáció Határozzuk meg a következő számok prímosztóit, a Fermat-féle faktorizációval n = 9448877023731667087211335518364761391339300989814358689662306401276 4822940429674896782148955029518662536728889008638515764940862999359 80058326676811553 n = 1386920455237926940734886251486503500461456554751374258493508115132 6766443476376668385111062500689154383628835526542738045702354571863 9851912645222024962467929356171411868321141914860902667248604530562 7352461109627499026042100536656859315307185957081533963552458544206 32543390563616964095891057540242178474763 n = 4668999961