Univerzita Karlova v Praze Matematicko-fyzikální fakulta DIPLOMOVÁ PRÁCE Hana Straková Testování perfektních mocnin Katedra algebry Vedoucí diplomové práce: RNDr. David Stanovský, Ph D. Studijní program: Informatika Studijní obor: Softwarové systémy 2008
Děkuji RNDr. Davidu Stanovskému, Ph D. za odborné vedení práce a za cenné připomínky a podněty. Dále děkuji Braňovi Bošanskému za pomoc s úpravou textu. Prohlašuji, že jsem svou diplomovou práci napsala samostatně a výhradně s použitím citovaných pramenů. Souhlasím se zapůjčováním práce a jejím zveřejňováním. V Praze dne 15. 4. 2008 Hana Straková 2
Obsah 1 Úvod 6 1.1 Perfektní mocniny......................... 6 1.2 Značení a definice......................... 8 1.3 Struktura práce.......................... 9 2 Algoritmy Bacha & Sorensona 10 2.1 Algoritmus A - binární vyhledávání a upravená Newtonova metoda............................... 10 2.2 Algoritmus B - sítové testy.................... 11 2.3 Algoritmus C - zkusmé dělení.................. 12 3 Algoritmus Daniela J. Bernsteina 15 3.1 Operace na číslech s plovoucí řádovou čárkou.......... 15 3.1.1 Sčítání, odčítání, násobení................ 15 3.1.2 Dělení, zaokrouhlování, umocňování........... 16 3.2 Bernsteinův algoritmus...................... 17 3.2.1 Funkce Nroot b....................... 17 3.2.2 Funkce Ppower...................... 19 3.2.3 Funkce Compare..................... 20 3.2.4 Hlavní cyklus Bernsteinova algoritmu.......... 21 4 Knihovna GMP 22 4.1 O knihovně GMP......................... 22 4.2 Aritmetické operace v knihovně GMP.............. 23 4.2.1 Násobení.......................... 23 4.2.2 Dělení........................... 24 4.2.3 Umocňování........................ 24 5 Implementace algoritmů 25 5.1 Implementace algoritmů Bacha & Sorensona.......... 27 5.2 Implementace Bernsteinova algoritmu.............. 29 3
6 Složitost algoritmů a výsledky měření 31 6.1 Algoritmus A........................... 32 6.1.1 Asymptotická složitost.................. 32 6.1.2 Výsledky měření..................... 34 6.2 Algoritmus B........................... 35 6.2.1 Čas běhu programu v průměrném případě....... 35 6.2.2 Výsledky měření..................... 39 6.3 Algoritmus C........................... 40 6.3.1 Čas běhu programu v průměrném případě....... 40 6.3.2 Výsledky měření..................... 42 6.3.3 Algoritmy B a C a mocniny prvočísla.......... 43 6.4 Bernsteinův algoritmus...................... 45 6.4.1 Složitost.......................... 45 6.4.2 Výsledky měření..................... 46 6.5 Funkce mpz_perfect_power_p.................. 47 7 Závěr 50 A Knihovna GMP 55 B Implementace algoritmů 59 B.1 Implementace algoritmů Bacha & Sorensona.......... 60 B.2 Implementace Bernsteinova algoritmu.............. 62 4
Název práce: Testování perfektních mocnin Autor: Hana Straková Katedra(ústav): Katedra algebry Vedoucí diplomové práce: RNDr. David Stanovský, Ph D. e-mail vedoucího: stanovsk@karlin.mff.cuni.cz Abstrakt: Perfektní mocnina je takové přirozené číslo, které lze zapsat jako netriviální mocninu jiného přirozeného čísla. Přirozené číslo n tedy nazveme perfektní mocninou, pokud existují přirozená čísla x a k, obě větší než 2, taková, že n = x k. Testy, zda je číslo perfektní mocnina, jsou důležité pro faktorizaci čísel nebo testy prvočíselnosti, které samy neumějí rozlišit, zda je vstupní číslo mocnina jiného čísla. Tato práce pojednává o dvou algoritmech na testování perfektních mocnin, prvním od E.Bacha & J. Sorensona a druhém od Daniela J. Bernsteina. Cílem práce je implementace algoritmů v jazyce C s pomocí knihovny GMP, srovnání teoretických výsledků pro jednotlivé algoritmy s praktickými měřeními a porovnání algoritmů mezi sebou z hlediska teoretického přístupu a rychlosti. Klíčová slova: perfektní mocnina, výpočetní teorie čísel, knihovna GMP Title: Perfect power testing Author: Hana Straková Department: Department of algebra Supervisor: RNDr. David Stanovský, Ph D. Supervisor s e-mail address: stanovsk@karlin.mff.cuni.cz Abstract: A positive integer n is a perfect power if there exist integers x and k, both at least 2, such that n = x k. Perfect power testing is important as preprocessing for number factorization and prime number testing, because many algorithms for that are not able to distinguish between prime number and power of prime number, so it is necessary to test it by perfect power tests. This thesis includes comparison of two algorithms for perfect power testing, one by Daniel J. Bernstein and the other by E. Bach & J. Sorenson. The goal is to implement described algorithms in C language with GMP library for multiple-precision arithmetics, to compare the theoretical results and running times of implemented algorithms. Keywords: perfect power, computational number theory, GMP library 5
Kapitola 1 Úvod 1.1 Perfektní mocniny Kryptografie a informační bezpečnost nabývají v současné době stále více na významu. Zatímco dříve byly slova jako šifra, klíč šifry, certifikáty vcelku neznámými pojmy, dneska se s nimi setkáváme prakticky denně, především ve spojení s informačními technologiemi. Méně známé už jsou detaily principů, na kterých jsou šifry založeny, informace o průběhu šifrování a dešifrování dat a zabezpečení informací. Jednu z velkých rolí hrají v kryptografii generátory náhodných čísel a testy prvočíselnosti. Na vlastnostech prvočísel stojí mnoho matematických teorií a prvočísla jsou základem pro mnoho šifer. Zmiňme alespoň nejpoužívanější a nejznámější asymetrickou šifru RSA. Důležitou část veřejného klíče této šifry tvoří číslo, které vznikne vynásobením dvou stejně velkých prvočísel (tak velkých, že výsledné číslo má zpravidla velikost 1024 nebo 2048 bitů). Znalost těchto dvou prvočísel tvoří pak soukromý klíč šifry. Prvočísla potřebná pro šifry se získávají tak, že se vygenerují libovolná čísla s požadovaným počtem cifer (pseudo)náhodným generátorem, a pak se na tato vygenerovaná čísla spustí test prvočíselnosti. Od testů prvočíselnosti očekáváme, že nám zjistí, zda vstupní číslo je nebo není prvočíslo. Problém nastává tehdy, pokud test prvočíselnosti není schopen rozlišit mezi prvočíslem a mocninou prvočísla, čili pozitivní odpověď je, že vstupní číslo je prvočíslo nebo mocnina prvočísla. Tak je tomu např. u moderního deterministického testu prvočíselnosti AKS (základní informace o testu AKS lze nalézt např. v [11]). V těchto případech se využije testování, zda vstupní číslo není mocninou nějakého jiného přirozeného čísla, tedy zda není perfektní mocninou. Přirozené číslo n nazýváme perfektní mocninou, pokud existují dvě přirozená čísla x a k, obě větší nebo rovna 2, taková, že platí vztah n = x k. Testy perfektních mocnin se používají nejen v testech prvočíselnosti, ale 6
i v algoritmech na faktorizaci čísel. Jeden z příkladů, kdy je potřeba před faktorizací čísla provést test, zda je číslo perfektní mocnina, je algoritmus faktorizující vstupní číslo n pomocí kongruence x 2 y 2 (mod n). Po vyřešení kongruence x 2 y 2 (mod n) a nalezení jejího řešení x a y, kde x je různé od ±y, je spočítán největší společný dělitel čísel n a x y, čímž dostaneme faktor čísla n. Taková řešení kongruence x a y budou existovat pouze v případě, že n není mocnina prvočísla. Je tedy nutno otestovat n, zda není perfektní mocninou. Pokud ne, můžeme hledat řešení kongruence, a pokud ano, tak máme rovnou nalezenu faktorizaci vstupního čísla n. Tato práce zkoumá čtyři algoritmy na testování perfektních mocnin. První tři algoritmy (označované jako algoritmus A, algoritmus B a algoritmus C) jsou popsány v práci Sieve Algorithms for Perfect Power Testing od Erica Bacha a Jonathana Sorensona [1] a čtvrtý algoritmus (označovaný jako Bernsteinův algoritmus) je popsán v práci Detecting Perfect Powers in Essentially Linear Time od Daniela J. Bernsteina [2]. Testy perfektních mocnin lze obecně rozdělit na tři základní druhy: detekční, dekompoziční a klasifikační. Detekční algoritmus je takový, který pro vstupní přirozené číslo n zjistí, zda n je nebo není perfektní mocnina. Dekompoziční algoritmus kromě toho, že zjistí, zda dané n je nebo není perfektní mocnina, najde v případě pozitivní odpovědi takové x, že x k = n. Klasifikační algoritmus se liší od dekompozičního v tom, že exponent k nalezený algoritmem je maximální. Algoritmy, kterými se zabýváme v této práci, spadají do kategorie dekompozičních algoritmů. Cílem práce je srovnání dvou odlišných přístupů k testování perfektních mocnin, implementace algoritmů v jazyce C s pomocí knihovny GMP (GNU Multiple Precision Arithmetic Library), změření reálného času běhu programů a srovnání výsledků s teoretickými výpočty. Součástí práce je i zkoumání chování programů, pokud jsou vstupními čísly pouze mocniny prvočísel. V rámci práce bylo rovněž provedeno srovnání sledovaných algoritmů s funkcí pro testování perfektních mocnin implementovanou v knihovně GMP. Bach & Sorenson ve svém článku předpokládali použití klasického algoritmu pro násobení, který má kvadratickou složitost vzhledem k velikosti násobených čísel. Implementace algoritmů v této práci ovšem používají k násobení čísel Karatsubův algoritmus s asymptotickou složitostí O(log n 1,585 ) pro vstupní číslo n (více informací o Karatsubově algoritmu např. na [12] nebo v [5]). Důvod je takový, že v dnešní kryptografii se nejvíce používají čísla do velikosti 1 000 cifer v desítkovém zápisu a pro čísla této velikosti je Karatsubův algoritmus nejefektivnější algoritmus pro násobení. V kapitole 6 jsou mimo praktických výsledků uvedeny i důkazy asymptotických složitostí algoritmů Bacha & Sorensona pro případ použití Karatsubova algoritmu. 7
1.2 Značení a definice V této části uvedeme značení, definice a věty používané v celé práci. Označení log n je použito pro logaritmus o základu 2, v ostatních případech je základ explicitně napsán. gcd(x,y) označuje největšího společného dělitele čísel x a y. V algoritmech Bacha & Sorensona je použit symbol, který má následující význam: p e n znamená, že platí p e n (p e dělí n beze zbytku), ale neplatí p e+1 n. U některých vět a lemmat je uvedeno označení (ERH). Tím je zdůrazněn fakt, že pro správnost věty či lemmatu je předpokládána platnost Rozšířené Riemannovy Hypotézy (Extended Riemann Hypothesis) (více informací na [10]). Pojmem klasická arimetika označujeme aritmetické operace s následující asymptotickou časovou složitostí. Předpokládáme, že a,b,m jsou přirozená čísla. Pak pro výpočet a + b, a b potřebujeme čas O(log a + log b) a pro výpočet a b, a/b, a mod b potřebujeme čas O(log a log b). Ze složitostí základních operací plynou i složitosti dalších operací, jako umocňování, kde pro výpočet c = a b potřebujeme čas O(log 2 c) a modulární umocňování a b mod m, pro které potřebujeme čas O(log a log m + log b log 2 m). V důkazech složitostí algoritmů v kapitole 6 budeme používat následující věty: Věta o prvočíslech (Prime Number Theorem) se týká hustoty prvočísel, čili jak jsou prvočísla distribuována. To, co budeme v důkazech složitosti algoritmů potřebovat, je počet prvočísel menších než nějaké číslo x. Z věty o prvočíslech plyne: π(x) x (1.1) ln x kde π(x) je počet prvočísel p x. Z Dirichletovy věty, někdy také nazývané Dirichletova věta o prvočíslech (Dirichlet s Prime Number Theorem), použijeme skutečnost, že v aritmetické posloupnosti čísel ve tvaru an + b, pro n = 1, 2,..., je nekonečně mnoho prvočísel, za předpokladu, že gcd(a,b) = 1. V důkazech v kapitole 6 použijeme Čebyševovu funkci (viz [13]) k odhadu p log n log p log n 8
Pro pochopení sítových testů v algoritmech Bacha & Sorensona, je potřeba malá Fermatova věta (Fermat s little theorem), která tvrdí, že platí a p 1 1 (mod p) kde p je prvočíslo, a je celé číslo a p nedělí a. 1.3 Struktura práce V kapitolách 2 a 3 jsou popsány studované algoritmy: kapitola 2 se zabývá třemi algoritmy Bacha & Sorensona a kapitola 3 je věnována Bernsteinovu algoritmu. K implementaci všech algoritmů byla použita knihovna GMP. Práce s touto knihovnou a její nastavení během sledování chování programů je popsáno v kapitole 4 a v dodatku A. V kapitole 4 lze nalézt informace o nastavení během měření a popis implementací aritmetických operací v této knihovně. V dodatku A jsou pak uvedeny podrobnosti k instalaci, způsob nastavování parametrů a zvyšování výkonu knihovny pomocí programu tuneup. Kapitola 5 a dodatek B jsou věnovány implementaci jednotlivých algoritmů. V kapitole 5 jsou zmíněny funkce použité v programech, princip generování čísel a prvočísel v programech, v dodatku B jsou podrobně popsány jednotlivé parametry funkcí včetně datových typů. Kapitola 6 analyzuje teoretickou složitost algoritmů a uvádí naměřené výsledky a jejich porovnání s teorií. V této kapitole jsou také dokázány složitosti algoritmů Bacha & Sorensona pro případ použití Karatsubova algoritmu pro násobení. Jsou tu uvedeny naměřené časy běhů jednotlivých programů se sledovanými algoritmy a pro některé algoritmy i výsledky pro případ, že na vstupu programu byly pouze mocniny prvočísel. Zde uvedeny výsledky testování funkce mpz_perfect_power_p, což je funkce pro testování perfektních mocnin implementovaná v knihovně GMP. Kapitola 7 ukazuje srovnání všech studovaných algoritmů z hlediska teoretických přístupů, teoretických časových náročností a naměřených rychlostí. 9
Kapitola 2 Algoritmy Bacha & Sorensona V článku Sieve Algorithms for Perfect Power Testing od Erica Bacha a Jonathana Sorensona [1] autoři vycházejí z nejjednoduššího algoritmu na testování perfektních mocnin, který postupně vylepšují přidáváním různých testů, čímž zlepšují asymptotickou složitost algoritmu. V této kapitole představíme jednotlivé algoritmy. Stanovení jejich složitostí je pak věnována kapitola 6. 2.1 Algoritmus A - binární vyhledávání a upravená Newtonova metoda Nejjednodušší algoritmus na testování perfektních mocnin spočívá v tom, že jsou počítány všechny možné odmocniny vstupního čísla n a zkoumá se, zda odmocnina z čísla n není přesně rovna nějakému přirozenému číslu. Pokud je takové přirozené číslo nalezeno, je vstupní číslo perfektní mocnina. První jednoduché zlepšení spočívá v tom, že nemusíme kontrolovat všechny odmocniny vstupního čísla, ale stačí zkoumat pouze možné prvočíselné odmocniny. Podíváme se podrobněji na hledání odmocniny vstupního čísla (v algoritmu A označena jako x). Popíšeme dvě verze algoritmu A. Jednodušší verze algoritmu používá k nalezení hodnoty x binární vyhledávání. Binární vyhledávání, někdy také názýváno půlení intervalů, je jedna z vyhledávacích metod. V našem případě hledáme v intervalu přirozených čísel hodnotu x takovou, že x je p-tá odmocnina vstupního čísla n pro právě prověřovaný prvočíselný exponent p. Pro pevně zvolené p postupujeme následovně. Najdeme hodnotu ležící ve středu aktuálně prohledáváného intervalu, kterou umocníme na p-tou, a výsledek pak porovnáme se vstupním číslem n. Podle toho, zda je mocnina menší nebo větší než n, pokračujeme s polovinou intervalu ležící nalevo nebo napravo od středu intervalu. V každém kroku se tedy 10
délka prohledávaného intervalu zmenší na polovinu. AlgoritmusA(n) 1 for each p prvočíslo, p log n do 2 spočti x = n 1/p ; 3 if (n = x p ) then 4 n je perfektní p-tá mocnina; 5 stop; 6 n není perfektní mocnina; Druhá verze algoritmu A používá k hledání odmocniny tzv. upravenou Newtonovu metodu. Newtonova metoda slouží k nalezení kořenu polynomu iterativním způsobem. Počáteční odhad kořene je v každé iteraci zpřesňován a pokračuje se v iterování, dokud není dosaženo požadované přesnosti odhadu kořene. V našem případě potřebujeme spočítat kořen polynomu f(y) = y p n. V každé iteraci algoritmus spočítá přesnější odhad kořene y i+1 = y i f(y i), f (y i ) kde y i je hodnota vypočítaná v předchozí iteraci. y 0 je počáteční odhad kořene polynomu f(y). Upravenou Newtonovou metodou budeme nazývat kombinaci binárního vyhledávání a Newtonovy metody. Prvních log p bitů hledané odmocniny x je spočítáno binárním vyhledáváním a výsledek je použit jako vstupní odhad pro Newtonovu metodu, pomocí které je číslo x dopočítáno na požadovanou přesnost. 2.2 Algoritmus B - sítové testy Algoritmus B vznikne vylepšením algoritmu A. Hlavní myšlenka úpravy algoritmu A vychází ze skutečnosti, že většina čísel nejsou perfektní mocniny a tedy algoritmus poběží rychleji, pokud se nám podaří vyloučit hned z počátku ta čísla, která perfektními mocninami nejsou. K tomu nám poslouží tzv. sítové testy (sieve tests). Vstupní číslo n projde pro daný exponent p testem, pokud je možné, že n je perfektní p-tá mocnina. Pokud číslo n testem neprojde, můžeme si být jisti, že n perfektní p-tá mocnina není. Taková čísla, která určitě nejsou perfektní p-té mocniny, budeme dále označovat jako neperfektní p-té mocniny. Očividným důsledkem malé Fermatovy věty je následující lemma: 11
Lemma 2.2.1. Nechť p a q jsou prvočísla, pro která platí q 1 (mod p). Dále předpokládejme, že n je perfektní p-tá mocnina a gcd(n,q) = 1. Potom n (q 1)/p 1 (mod q). Výpočet n (q 1)/p budeme označovat jako sítový test pro číslo n a prvočíslo p. Číslo q nazveme modulem pro sítový test. Číslo n projde testem, pokud n (q 1)/p 0, 1 (mod q). V opačném případě n perfektní p-tá mocnina být nemůže a lze přejít na testování dalšího exponentu. Sítových testů provedeme pro každý exponent p a vstupní číslo n nejvýše 2log log n log p. Pokud n prošlo všemi sítovými testy pro exponent p, pak je možné, že je to perfektní p- tá mocnina. Číslo 2 log log n log p je zvoleno jako kompromis mezi tím, aby bylo vyloučeno co nejvíce neperfektních p-tých mocnin, ale aby sítových testů nebylo provedeno příliš mnoho. Jeho bližší zdůvodnění lze nalézt v [1]. AlgoritmusB(n) 1 for each p prvočíslo, p log n do 2 proveď 2log log n log p sítových testů na n; 3 if (n prošlo všemi testy) then 4 spočti x = n 1/p ; 5 if (n = x p ) then 6 n je perfektní p-tá mocnina; 7 stop; 8 n není perfektní mocnina; V algoritmu B se pro nalezení odmocniny používá upravená Newtonova metoda. 2.3 Algoritmus C - zkusmé dělení Algoritmus B popsaný v předešlé části jsme dostali doplněním základního algoritmu A na hledání perfektních mocnin o sítové testy, které vyloučily na začátku některá čísla, která nejsou perfektní mocniny pro nějaký exponent p. Algoritmus C získáme zlepšením algoritmu B přidáním zkusmého dělení (trial division). Zkusmým dělením je myšlena procedura, kde je testováno, zda r n, pro prvočísla r b. b je číslo zvoleno tak, aby bylo o dost menší než n, řádově log n. Číslo b je zvoleno jako nejmenší číslo takové, pro které platí b log 2 b log n 12
. Po provedení zkusmého dělení nastane jedna ze dvou situací: (a) Najdeme prvočíslo r takové, že r n. V tomto případě spočítáme exponent e, pro který platí r e n. Je-li vstupní číslo n perfektní p-tá mocnina, pak musí platit, že p e. Protože e je relativně malé číslo, takových p, že p dělí e, je málo. Tedy se oproti algoritmu B značně zmenší počet takových prvočíselných exponentů p, které musí být dále prověřeny. (b) Není nalezen žádný prvočíselný dělitel čísla n menší než b. Potom víme, že pokud je n perfektní p-tá mocnina, pak p-tá odmocnina z n (označme ji x), musí být větší než b. A tedy b p x p = n log b b p log b n p log b n p log n, čímž se též zmenší počet exponentů k prověřování. log b V algoritmu C je pro hledání odmocniny opět použita upravená Newtonova metoda. q v algoritmu značí prvočíselný modulus pro sítové testy. 13
AlgoritmusC(n) 1 Spočti nejmenší přirozené číslo b : b log 2 b log n; 2 S {p : p log n }; log b 3 for each r prvočíslo, r b do 4 if (r n) then 5 najdi e : r e n; 6 S {p : p e}; 7 ukonči dělení; 8 while (S ) do 9 p = nejmenší prvek S; 10 proveď 2log log n log p sítových testů na n; 11 if (q n) then 12 najdi e : q e n ; 13 S S {p : p e}; 14 if ((n prošlo všemi testy) & ( p S )) then 15 x = n 1/p ; 16 if (n = x p ) then 17 n je perfektní p-tá mocnina; 18 stop; 19 S S {p}; 20 číslo n není perfektní mocnina; 14
Kapitola 3 Algoritmus Daniela J. Bernsteina Dalším algoritmem pro testování perfektních mocnin je algoritmus Daniela J. Bernsteina popsaný v článku Detecting Perfect Powers in Essentially Linear Time [2]. Bernstein díky upravení některých základních funkcí docílil v podstatě lineární složitosti algoritmu (essencially linear time). Protože v algoritmu nejsou použita pouze přirozená čísla, ale také čísla s plovoucí řádovou čárkou (floating-point numbers), první část této kapitoly je zaměřena na tato čísla a na operace s nimi. Potom následuje popis jednotlivých funkcí Bernsteinova algoritmu. Při popisu algoritmů a funkcí v celé kapitole platí, není-li uvedeno jinak: b,k,n,n,x jsou přirozená čísla a,a jsou celá čísla p je prvočíslo y,r,r jsou čísla s plovoucí řádovou čárkou 3.1 Operace na číslech s plovoucí řádovou čárkou Číslo s plovoucí řádovou čárkou r je reprezentováno dvojicí (a,n), a Z a n N, tak, že platí r = 2 a n. Reprezentaci čísla s plovoucí řádovou čárkou dostaneme vydělením mocninou čísla 2. 3.1.1 Sčítání, odčítání, násobení Jsou-li r a r dvě čísla s plovoucí řádovou čárkou reprezentovaná dvojicemi (a,n) a (a,n ), čili r = 2 a n a r = 2 a n, pak aritmetické operace sčítání, 15
odčítání a násobení čísel r a r vypadají následovně. r + r = 2 f (2 a f n + 2 a f n ) r r = 2 f (2 a f n 2 a f n ) r r = 2 a+a nn f = min(a,a ) a předpokládáme, že r r. 3.1.2 Dělení, zaokrouhlování, umocňování Funkce pro dělení div b (r,k) vrací takové číslo s plovoucí řádovou čárkou, které aproximuje podíl r r tak, že platí (1, 1 + k k div b (r,k) 21 b ). Funkci definujme takto: div b (2 a n,k) = 2 a+f log k b n 2 f log k b k f je číslo takové, že pro vstupní číslo r = 2 a n platí 2 f 1 n < 2 f. Funkci pro zaokrouhlování čísla na b bitů označíme trunc b (r). Zaokrouhlení r na b bitů znamená, že (1, 1 + trunc b (r) 21 b ). Funkci definujme: n trunc b (2 a n) = div b (2 a n, 1) = 2 a+f b 2 f b f je číslo takové, že pro vstupní číslo r = 2 a n platí 2 f 1 n < 2 f. Funkci pro umocňování značíme pow b (r,k). Jedná se o b-bitovou aproximaci čísla r k, čili pow b (r,k) r k < pow b (r,k)(1 + 2 1 b ) 2k 1. Funkce je definována následujícím způsobem: pow b (r, 1) = trunc b r pow b (r, 2k) = trunc b (pow b (r,k) 2 ) pow b (r, 2k + 1) = trunc b (pow b (r, 2k)pow b (r, 1)) 16
3.2 Bernsteinův algoritmus Bernsteinův algoritmus vychází ze stejné myšlenky jako algoritmus A Bacha & Sorensona. Jedná se o základní algoritmus na počítání perfektních mocnin, který má na vstupu přirozené číslo n: 1 for each p prvočíslo, p < log 2n do 2 zkontroluj, zda n je p-tá mocnina; Názvy funkcí Bernsteinova algoritmu jsou změněny oproti původním názvům v článku tak, aby se funkce nepletly s názvy algoritmů Bacha & Sorensona. Pro lehčí orientaci v původním článku však u každé funkce uvedeme i název použitý v článku D. Bernsteina. K pochopení celého algoritmu je zapotřebí podrobně popsat tři funkce. Nroot b (y,k) spočítá aproximaci čísla y 1/k Ppower(n,k,y) zjistí, zda n je k-tá mocnina; v y je předem vypočtená aproximace hodnoty n 1. (Původní označení funkce je Algoritmus K.) Compare(n,x,k) zjistí, zda n = x k ; funkce Compare je použita ve funkci Ppower. (Původní označení funkce je Algoritmus C.) 3.2.1 Funkce Nroot b Vstupními argumrnty funkce Nroot b jsou číslo s plovoucí řádovou čárkou y, k N a b N. Funkce spočítá s přesností b aproximaci čísla y 1/k. Zadanou přesností se podobně jako u ostatních Bernsteinových funkcí myslí, že výstup funkce splňuje podmínku: (1 2 b )Nroot b (y,k) < y 1/k < (1 + 2 b )Nroot b (y,k). Pro počítání odmocniny zvolil Bernstein, stejně jako Bach & Sorenson, kombinaci binárního vyhledávání a Newtonovy metody. Výsledná funkce je pojmenována Nroot b a počítá aproximaci čísla y 1/k. Binární vyhledávání označíme BinarySearch (v článku označeno jako Algoritmus B) a Newtonovu metodu NewtonsMethod (v článku označena jako Algoritmus N). 17
V každém kroku binárního vyhledávání je prohledávaný interval, ve kterém hledaný kořen leží, zmenšen na polovinu. Nejprve je aproximována hodnota výrazu z k y a vybrána levá nebo pravá polovina intervalu podle toho, zda je výraz větší či menší než 1. Bernstein ještě přidává podmínku, že pokud je hodnota výrazu z k y příliš blízko 1 pro zkoumané z, nevybere pravou nebo levou polovinu intervalu, ale použije střední část intervalu. Čili část intervalu okolo středu, jejíž velikost je opět polovina původního intervalu. Hodnota výrazu z k y není vyčíslována přesně, ale pouze aproximována, čímž je dosaženo velkého zrychlení výpočtu. BinarySearch(b, k, y) 1 najdi g : 2 g 1 < n 2 g ; 2 a = g ; k 3 B = log(66(2k + 1)) ; 4 z = 2 a + 2 a 1 ; 5 j = 1; 6 while (j < b) do 7 r = trunc B (pow B (z,k)trunc B (y)); 8 if (r 993 ) then 1024 9 z = z 2 a j 1 ; 10 else if (r > 1) then 11 z = z + 2 a j 1 ; 12 j = j + 1; 13 return z; Bernstein používá ve svém algoritmu mnoho různých konstant. Ne vždy je přesně vysvětleno, proč je použita právě daná konstanta. Většina konstant byla zvolena kvůli odhadům v důkazech složitostí a správností jednotlivých 993 funkcí. Jednou z takových konstant je např. i použitá v binárním vyhledávání. Bernstein její použití zdůvodňuje tím, že je číslo 993 je nejjednodušší 1024 1024 číslo s plovoucí řádovou čárkou v rozmezí mezi 32 a e 1 33, což pak používá v 33 důkazech rychlosti a správnosti funkce BinarySearch, které lze nalézt v [2]. Cílem Newtonovy metody je najít kořen polynomu h(z) = z k y 1 1, čili číslo y 1/k. Prvotní odhad kořene z je v každém cyklu metody nahrazován přesnější hodnotou z new = z h(z) h (z) = z + (z yzk+1 ). Algoritmus je iterován tak dlouho, dokud z nemá požadovanou přesnost. Níže popsaná funkce NewtonsMethod je tedy ta část Newtonovy metody, která je stále iterována, až je z dostatečně blízko správnému řešení. Každá iterace Newtonovy metody 18
NewtonsMethod(b,k,y,z) 1 r 2 = mul(trunc b (z),k + 1); 2 r 3 = trunc b (pow b (z,k + 1)trunc b (y)); 3 r 4 = div b (r 2 r 3,k); 4 return r 4 ; přibližně zdvojnásobí počet správných číslic výsledku. Funkce mul(y, k) značí násobení čísla s plovoucí řádovou čárkou y přirozeným číslem k. Binární vyhledávání a Newtonova metoda je spojena do výsledné funkce Nroot b obdobně, jako tomu bylo u upravené Newtonovy metody. V Bernsteinově algoritmu je prvních 3 + log k bitů kořene z odhadnuto pomocí binárního vyhledávání a zbytek upřesněn Newtonovou metodou. Nroot b (k,y) 1 if b < 4 + log k then 2 z = BinarySearch b (y,k); 3 else (b log k ) 2 ; 4 b = log 2k + 5 B = 2b + 4 log k ; 6 if (b < 4 + log k ) then 7 z = BinarySearch b (y,k); 8 else 9 z = Nroot b (y,k); 10 z = NewtonsMethod B (y,k,z); 11 return z; 3.2.2 Funkce Ppower Funkce Ppower, v článku původně označovaná jako Algoritmus K, zjišťuje, zda n = x k. Vstupními parametry funkce jsou přirozená čísla n a k a číslo s plovoucí řádovou čárkou y. y je pomocná proměnná, ve které je uložena předpočítaná aproximace čísla n 1, která se pak využije během výpočtu. Funkce Ppower nejprve spočítá aproximaci r čísla n 1/k. Pokud existuje přirozené číslo 19
x takové, že x r < 1 4, zkusí, zda xk = n. Ppower(n,k,y) 1 f = log 2n ; 2 b = 3 + f ; k 3 r = Nroot b (y,k); 4 najdi x N : x r 5; 8 5 if ((x = 0) or ( x r 1 )) then return 0; 4 6 else 7 s = Compare(n,k,x); 8 if (s = 0) then return x; 9 else return 0; Funkce Compare, podrobně popsaná v další části, vrací znaménko výrazu n x k. Tedy pokud s je rovno 0, znamená to, že n = x k a tedy, že n je k-tá mocnina. Funkce Ppower vrací číslo n 1/k, pokud je n k-tá mocnina, v ostatních případech vrací 0. Kompletní důkaz správnosti funkce lze najít v [2]. 3.2.3 Funkce Compare Compare(n,k,x) 1 f = log 2n ; 2 b = 1; 3 while (b < f) do 4 r = pow b+ log 8k (x,k); 5 if (n < r) then return -1; 6 if (r(1 + 2 b ) n) then return 1; 7 b = min{2b,f}; Vstupními argumenty funkce Compare, která je v článku původně označená jako Algoritmus C, jsou přirozená čísla n, x a k. Funkce testuje rovnost čísel n a x k. Návratová hodnota funkce je znaménko výrazu n x k. Od klasických funkcí na porovnávání čísel se funkce Compare liší v tom, že 20
výraz x k nevyčísluje přesně, ale používá z něj pouze tolik prvních číslic, kolik potřebuje. Čím rozdílnější čísla jsou, tím rychleji je to rozpoznáno, a mocnina nemusí být dopočítána přesně, čímž se algoritmus urychlí. Důkaz správnosti funkce lze opět najít v [2]. 3.2.4 Hlavní cyklus Bernsteinova algoritmu Jelikož byly již všechny potřebné funkce popsány, můžeme nyní napsat celý Bernsteinův algoritmus. Hlavní cyklus Bernsteinova algoritmu je v článku označován jako Algoritmus X, my ho budeme nazývat BernsteinsAlgorithm. BernsteinsAlgorithm(n) 1 f = log 2n ; 2 y = nroot 3+ f/2 (n, 1); 3 for each p prvočíslo, p < f do 4 x = Ppower(n,p,y); 5 if (x > 0) then 6 n je perfektní p-tá mocnina (n = x p ); 7 n není perfektní mocnina; 21
Kapitola 4 Knihovna GMP Všechny algoritmy popsané v předchozích kapitolách jsme implementovali v jazyce C s pomocí funkcí knihovny GMP (GNU Multi-Precision Library) pro práci s velkými čísly. Cílem této kapitoly je seznámení s knihovnou GMP, popis jejích základních charakteristik a způsobu použití při implementaci algoritmů. V této kapitole je také popsána implementace některých aritmetických operací v knihovně GMP imlementovány, jak je to důležité pro naše testování a jak vypadalo nastavení knihovny v průběhu měření času běhu programů. Některé dodatečné informace, jako např. poznámky k instalaci knihovny, informace o nastavování jednotlivých parametrů knihovny apod. jsou podrobněji popsány v dodatku A. 4.1 O knihovně GMP GNU Multi-Precision Library je knihovna určená k práci s libovolně velkými čísly nebo spíše čísly s libovolnou přesností (arbitrary precision arithmetic). Velikost čísel, se kterými jsou knihovní funkce schopny pracovat, je omezena typicky jen používaným hardwarem, především velikostí dostupné paměti. V knihovně GMP jsou implementovány funkce pro různé typy čísel. Nejpoužívanější jsou přirozená čísla, racionální čísla a čísla s plovoucí řádovou čárkou. Velkou předností knihovny je efektivita implementovaných operací a tedy i rychlost. Kvůli rychlosti operací a téměř neomezené velikosti čísel se knihovna GMP hodí především pro kryptografické aplikace, pro aplikace sloužící k zabezpečení komunikace přes Internet, pro implementaci algoritmů z výpočetní teorie čísel apod. Z projektů používajících knihovnu GMP uveďme např. knihovnu MPFR pro práci s čísly s plovoucí řádovou čárkou, knihovnu NTL pro teorii čísel nebo projekt Kaffe Java Virtual Machine. Seznam projektů používajících knihovnu GMP lze nalézt na oficiálních stránkách této 22
knihovny [6]. 4.2 Aritmetické operace v knihovně GMP Rychlost knihovny GMP je založena především na skutečnosti, že se použité algoritmy pro aritmetické operace mění v závislosti na velikosti operandů nebo na vlastnostech operandů. Protože naším cílem je zkoumat chování algoritmů, je třeba vysvětlit, jak jsou jednotlivé operace, jako násobení, dělení nebo umocňování v knihovně GMP implementovány a jaké algoritmy jsme během sledování chování algoritmů použili. 4.2.1 Násobení Pro násobení jsou v knihovně GMP implementovány celkem 4 algoritmy: klasický školský algoritmus, Karatsubův algoritmus, algoritmus Toom3 a FFT. Popis jednotlivých algoritmů lze nalézt v [5] nebo v [7]. Použitý algoritmus pro násobení dvou čísel je vybrán v závislosti na velikosti operandů. Hranice použití jednotlivých algoritmů jsou dané nastavením parametrů: MUL_KARATSUBA_THRESHOLD MUL_TOOM3_THRESHOLD MUL_FFT_THRESHOLD Velikost čísel v parametrech se udává v tzv. limbech. Limbem se v knihovně GMP rozumí taková část čísla, která se vejde do jednoho strojového slova, tedy obvykle 32 bitů. Velikost limbu se dá nastavit parametry BITS_PER_LIMB a BYTES_PER_LIMB. Knihovna má implicitně nastaveny hodnoty konstant pro různé třídy procesorů a jsou vybrány ty konstanty, které nejlépe vyhovují danému hardwaru. Konstanty je však možno změnit. Způsob nastavení konstant a případného zlepšení výkonu knihovny pomocí programu tuneup jsou popsány v dodatku A. Za účelem zkoumání chování algoritmů jsme nastavili konstanty tak, aby pro čísla ve zkoumaném rozmezí byl pro násobení použit pouze Karatsubův algoritmus. Tento algoritmus má asymptotickou složitost O(log log 2 3 n) = O(log 1,585 n), kde n je vstupní číslo. 23
4.2.2 Dělení Pro dělení čísel je také v knihovně GMP implementováno několik algoritmů, ale změna použitého algoritmu není dána vzrůstající velikostí operandů, jak tomu bylo u násobení. Různé algoritmy jsou většinou použity pro speciální případy, např. pokud je dělenec dvakrát větší než dělitel nebo pokud předem víme, že dělení vyjde beze zbytku. V našem případě nemůžeme zaručit, že při dělení nastane pouze nějaký speciální případ, a tedy počítáme s tím, že asymptotická složitost dělení je kvadratická, jako u klasické aritmetiky. 4.2.3 Umocňování Použitý algoritmus násobení ovlivňuje složitost dalších operací, především umocňování. Knihovna GMP používá pro umocňování algoritmus umocni a vynásob (square and multiply) variantu zleva doprava (left to right). Vstupními argumenty jsou přirozená čísla k a e. Výstupem funkce je číslo n = k e. Binární zápis exponentu e označíme (e t,e t 1,...,e 0 ) 2, čili e i značí jednotlivé bity v binárním zápisu exponentu e. Square_and_multiply(k, e) 1 res = 1; 2 for (i = t downto 0) do 3 if (e i = 1) then 4 A = k res res; 5 else res = res res; 6 return res; V algoritmu je provedeno celkem t + 1 cyklů a v každém z nich se násobí čísla nejvýše rovná výsledku n. t + 1 = log 2 e je počet bitů exponentu e. Umocňování umocni a vynásob má tedy asymptotickou složitost O(log e log α n), kde log α n je složitost násobení. V našem případě počítáme α = 1, 585, jelikož počítáme s Karatsubovým algoritmem. 24
Kapitola 5 Implementace algoritmů V této kapitole je popsána vlastní implementace jednotlivých algoritmů - řešení funkcí, použité struktury a datové typy, názvy funkcí v programech apod. Další podrobnosti týkající se implementace, jako přesné názvy funkcí, parametry funkcí, datové typy parametrů apod. jsou popsány v dodatku B. Datové typy a proměnné V implementaci algoritmů popsaných v článku Bacha & Sorensona byly použity klasické datové typy jazyka C: int unsigned long signed long mpz_t a datový typ knihovny GMP: Datový typ mpz_t slouží k ukládání velkých přirozených čísel. Tento typ byl použit především pro uložení vstupních čísel. Vstupní čísla jsou uložena v poli input_arr zmíněného typu, tedy mpz_t. V Bernsteinově algoritmu byly kromě výše zmíněných typů použity ještě dva další datové typy. mpf_t je datový typ knihovny GMP, který slouží pro ukládání velkých čísel s plovoucí řádovou čárkou. 25
Dále byla použita struktura struct { long a; mpz_t n; } fp; pro ukládání čísel y = 2 a n. V úvodu bylo zmíněno, že popisované algoritmy jsou dekompoziční, tedy že algoritmus vrací přirozená čísla x a p v případě, že vstupní číslo n = x p. Pro toto slouží v programech pole base a exponent. Pokud je vstupní číslo input_arr[i] rozpoznáno jako perfektní mocnina, pak je do base[i] uloženo číslo x a do exponent[i] číslo p. Generování prvočísel Ve všech algoritmech potřebujeme mít k dispozici seznam prvočísel. Prvočísla potřebná v programech jsou generována pomocí funkce knihovny GMP nextprime. Všechna prvočísla vygenerovaná touto funkcí a používaná v programech jsou uložena v poli primes, kde primes[0] = 2, primes[1] = 3, primes[2] = 5 atd. V algoritmu A a v Bernsteinově algoritmu jsou zapotřebí pouze prvočísla menší než log n, kde n je vstupní číslo. U algoritmů B a C jsou potřeba ještě další prvočísla pro vytváření tabulky modulů pro sítové testy. Generování mocnin prvočísel Jedním ze sledovaných výstupů je chování algoritmů B a C v situaci, kdy vstupními čísly jsou pouze mocniny prvočísel. Pro generování mocnin prvočísel o určité velikosti jsme použili dvě konstanty: LOWER_NUMBER a UPPER_NUMBER, které vymezují požadovanou velikost generovaných mocnin prvočísel. Ze seznamu prvočísel je náhodně vybráno jedno, označme ho p, a je nalezen nejmenší exponent k takový, že 2 UPPER_NUMBER < p k. Pak je náhodně vybrán exponent velikosti nejvýše k, a pokud 2 UPPER_NUMBER < p k, pak mocnina p k spadá do požadovaného rozmezí. Vygenerovali jsme tedy další vstupní číslo, které uložíme do pole input_number. Generování vstupních čísel Parametry pro generování vstupních čísel (např. kolik čísel má být vygenerováno a kolik cifer mají vygenerovaná čísla mít) se v programu nastavují konstantami: 26
NUMBERS počet generovaných čísel BASE_NUM číselná soustava, ve které jsou čísla generována DIGITS počet cifer generovaných čísel MAX konstanta pro statickou alokaci polí FIRST_PRIME první prvočíslo ROUNDS pro testovací účely; nastavení, kolikrát se mají výpočty opakovat Konstanty byly při testování nastaveny následujícím způsobem: BASE_NUM 10 MAX 1 000 000 FIRST_PRIME 2 ROUNDS 1 Konstanta MAX byla v programech použita pro omezení velikosti pole vstupních čísel a pole prvočísel. Její velikost byla odvozena od počtu prvočísel odpovídajícímu maximálnímu modulu pro sítové testy a počtu generovaných vstupních čísel. Vstupní čísla pro programy jsou generována po jednotlivých cifrách v soustavě určené hodnotou BASE_NUM. Cifry vstupních čísel jsou generovány pomocí funkce knihovny GMP mpz_urandomm a vstupní čísla jsou ukládána do pole input_arr. Měření času Čas programů byl měřen pomocí knihovny time.h a funkce clock(). U každého implementovaného algoritmu jsme sledovali závislost doby běhu programu na délce vstupních čísel. Výsledky měření jsou uvedeny v následující kapitole. 5.1 Implementace algoritmů Bacha & Sorensona Binární vyhledávání, upravená Newtonova metoda Ve všech algoritmech Bacha & Sorensona je použito binární vyhledávání a téměř ve všech upravená Newtonova metoda, proto tu popíšeme některé detaily týkající se těchto procedur. 27
Pro binární vyhledávání je potřeba nastavit na začátku spodní a horní mez intervalu, kde se hodnota má hledat. V našem případě je spodní mez stanovena na hodnotu 2, horní na 2 log n/p +1. V algoritmu, kde není použita Newtonova metoda, je binární vyhledávání iterováno, dokud se spodní a horní mez nerovnají, nebo dokud není pro daný prvočíselný exponent p nalezeno y, že y p = n, kde n je vstupní číslo a p je prověřovaný exponent. V dalších algoritmech s upravenou Newtonovou metodou je pro dané prvočíslo p počítáno pouze log p iterací binárního vyhledávání a zbytek je dopočítán upravenou Newtonovou metodou. Pomocí upravené Newtonovy metody počítáme kořen rovnice f(y) = y p n, kde y je hledaná odmocnina, p je prověřovaný prvočíselný exponent a n je vstupní číslo. První odhad y získáme z binárního vyhledávání, následující zpřesňované odhady jsou počítány Newtonovou metodou. V každé iteraci Newtonovy metody je spočten nový přesnější odhad y next = y f(y) f (y) = (p 1)xp n px p 1 Pokud je počítáno s racionálními čísly, Newtonova metoda bývá implementována tak, že je iterace prováděna tak dlouho, dokud hodnota f(y) = y p n není dostatečně blízko nuly. Protože náš algoritmus počítá pouze s přirozenými čísly, bylo třeba koncové podmínky upravit. V každé iteraci Newtonovy metody je spočítán zpřesněný odhad kořene y, který je umocněn na právě prověřovaný prvočíselný exponent p. Výsledná mocnina je porovnána se vstupním číslem. Pokud se rovnají, tak je vstupní číslo n perfektní mocnina, neboť je rovno y p, a výpočet končí. Výpočet také končí, pokud je dvakrát za sebou spočten stejný odhad kořene y, protože se odhad nedá v přirozených číslech lépe zpřesnit, a nebo pokud je y p < n. Zdůvodnění je takové, že funkce f(y) = y p n je napravo od nuly konkávní a jelikož původní odhad z binárního vyhledávání je hledán v intervalu 2, 2 log n/p +1, pohybujeme se napravo od nuly. Ke kořeni se tedy blížíme zprava v tom smyslu, že odhad y stále zmenšujeme. Pokud platí y p < n, jsme už nalevo od kořene, přesný kořen jsme tedy minuli, a tedy n není perfektní p-tá mocnina. Funkce vrací 1, pokud je číslo perfektní mocnina, 0 v ostatních případech. Sítové testy V algoritmu B jsou oproti algoritmu A přidány sítové testy. Je tedy třeba generovat moduly pro tyto sítové testy, čili taková prvočísla q, že q 1 (mod p), a také reprezentovat tabulku takovýchto modulů. K uložení hodnot pro každé prvočíslo p slouží struktura 28
struct sieve_moduli { unsigned long number_sm; unsigned long * sm; } Pro každé prvočíslo p log n potřebujeme celkem 2 log log n log p modulů, pro každý sítový test jeden. Tabulku uložíme do pole struktur sieve_moduli. sieve_moduli[i] slouží pro uložení modulů pro prvočíslo, které je uloženo v p =primes[i]. Vnumber_sm je uložena hodnota 2 log log n log p a vsm je uložen jejich seznam. Pro vytvoření takovéto tabulky pro sítové testy slouží funkce fill_sieve_table a funkce sieve_tests slouží pro provádění vlastních sítových testů. Zkusmé dělení Funkce týkající se zkusmého dělení přidané do algoritmu C jsou funkce trial_division, která zkouší nalézt prvočíselného dělitele vstupního čísla, a dále funkce find_e pro nalezení exponentu e pro nějaké prvočíslo r tak, že r e n, kde n je vstupní číslo. Další přidané funkce jsou určeny pro práci s polem S, ve kterém jsou uložena taková prvočísla p, pro která je možné, že platí n = y p, pro nějaké přirozené číslo y. 5.2 Implementace Bernsteinova algoritmu V Bernsteinově algoritmu byly pro nalezení p-té odmocniny z čísla n zkombinovány Newtonova metoda a binární vyhledávání do funkce Nroot b, která počítá aproximaci čísla n 1/p. Pro b 3+ log p použije binární vyhledávání, pro vyšší b pak Newtonovu metodu. Na začátku binárního vyhledávání je třeba stanovit interval, ve kterém se číslo bude hledat. V Bernsteinově algoritmu se na začátku binárního vyhledávání spočítá funkcí find_g exponent g takový, že 2 g 1 < n 2 g, a položí se a = g, takže platí, že p 2a n 1/p < 2 a+1, a z tohoto intevalu se pak nadále v binárním vyhledávání vychází. V Newtonově metodě je v každé iteraci nahrazen odhad kořene z novým, přesnějším odhadem z new = z h(z) = z + (z h (z) yzk+1 ). Hodnota je počítána pomocí funkcí trunc_b, pow_b a div_b, čili výrazy nejsou vyčíslovány přesně, ale vždy jen podle parametru b. Ten udává, na kolik bitů se má výsledek spočítat, což urychluje výpočty. Velká část implementace Bernsteinova algoritmu jsou funkce pro práci s vlastním definovaným typem fp, který slouží pro ukládání čísel y = 2 a n. 29
Bylo potřeba implementovat aritmetické operace na číslech takto reprezentovaných, porovnávání čísel a také konverzi z datového typu mpz_t na tuto strukturu. Funkce pro aritmetické operace s tímto datovým typem jsou následující: fp_add pro sčítání, fp_sub pro odčítání, fp_mul pro násobení čísel a dále div_b pro dělení, trunc_b pro zaokrouhlování a pow_b pro umocňování. Všechny tyto funkce byly do detailů popsány v kapitole 3. Další potřebné funkce jsou funkce cmp_fp_fp a cmp_fp_int pro porovnávání čísel reprezentovaných strukturou fp jak mezi sebou, tak i s čísly typu mpz_t. První slouží pro porovnávání dvou čísel r1 a r2 reprezentovaných strukturou fp, druhá funkce slouží pro porovnávání čísla r1 ve struktuře fp s číslem r2 typu mpz_t. V obou případech je návratová hodnota kladné číslo, pokud je r1 větší než r2, nula, pokud jsou si čísla rovná, a záporné číslo, pokud r1 je menší než r2. Ve funkcích pro dělení a zaokrouhlování nebo v binárním vyhledávání jsou v Bernsteinově algoritmu zapotřebí takové exponenty f a g, že 2 f n < 2 f, resp. 2 g 1 < y 2 g. V obou případech je exponent počítán pro číslo y = 2 a n. K nalezení takovýchto exponentů slouží funkce find_f a find_g. Tyto funkce mají k dispozici předpočítaná pole pow2 a pow2_zap, kde jsou uloženy mocniny čísla 2 a pomocí kterých se dané exponenty hledají. Funkce find_x nalezne ke vstupnímu číslu r s plovoucí řádovou čárkou takové přirozené číslo x, že x r 5/8. 30
Kapitola 6 Složitost algoritmů a výsledky měření Cílem této kapitoly je prezentovat výsledky testování algoritmů a porovnat naměřené hodnoty s teoretickými výpočty. U každého algoritmu z článku Bacha & Sorensona jsou dokázány teoretické výpočty pro případ Karatsubova násobení. Teoretické výsledky pro případ klasického násobení jsou pouze uvedeny, dokázány jsou v [1]. V důkazech používáme značení O(log α n) pro asymptotickou složitost násobení, případně O(log α n log p) pro asymptotickou složitost umocňování čísla n p. V našem případě je α = 1, 585, protože počítáme s Karatsubovým algoritmem. Nejdůležitější z hlediska kryptografie jsou čísla do cca 2 000 decimálních cifer. Dnes je např. pro šifru RSA používán klíč velikosti 1 024 nebo 2 048 bitů, což jsou čísla o přibližně 300 a 600 decimálních cifrách, a předpokládá se, že v blízké budoucnosti budou stačit klíče o velikost 4 096 bitů, což je okolo 1 500 decimálních cifer. Tato čísla jsou však pro sledování asymptotického chování algoritmů přiliš malá, proto jsou výsledky každého algoritmu prezentovány na číslech od 1 000 do 10 000 cifer. U algoritmů B a C se během testování ukázala i velikost 10 000 decimálních cifer nedostatečná pro stanovení asymptotického chování algoritmu, proto jsou u těchto dvou algoritmů prezentovány výsledky až do 15 000 decimálních cifer. U každého programu je ještě pro zajímavost uvedeno, jak dlouho trvalo zpracování 500 ciferných čísel, která jsou pro dnešní kryptografii podstatná. Důvodem, proč nemohla být čísla do 1 000 cifer zahrnuta do zkoumání chování algoritmů, je, že nejnižší hodnota parametru MUL_KARATSUBA_THRESHOLD, jehož význam je popsán v kapitole 4, je 10 limbů, což odpovídá přibližně 100 ciferným číslům. Protože Karatsubův algoritmus při násobení dělí čísla na poloviny, při násobení malých čísel se ještě znatelně projeví použití škol- 31
ského násobení čísel pod 100 cifer. V každém grafu jsou vyneseny časy (v sekundách) v závislosti na velikosti čísel (počet decimálních cifer). Pro každý algoritmus byl měřen jiný počet vstupních čísel; konkrétní počet je uveden u jednotlivých algoritmů. Pro každý algoritmus byly naměřené hodnoty proloženy funkcí vystihující jejich průběh. K tomu byla použita regresní analýza (metoda nejmenších čtverců) implementovaná v programu MS Excel. Pro algoritmy Bacha & Sorensona byly použity mocninné funkce, pro Bernsteinův algoritmus funkce lineární. Programy byly spuštěny na procesoru AMD Athlon 64 X2 Dual Core Processor 3800+ s 32-bitovým jádrem (konstanta BITS_PER_LIMB byla nastavena na hodnotu 32). Verze použité knihovny GMP byla 4.2.1. 6.1 Algoritmus A 6.1.1 Asymptotická složitost Pro důkaz složitosti algoritmu A s binárním vyhledáváním využijeme rovnost: p b kde p jsou prvočísla (viz [4]). 1 p = log log b + O(1) (6.1) K důkazu složitosti algoritmu A s upravenou Newtonovou metodou využijeme následující lemma. Lemma 6.1.1. Buď f(x) = x p n, r > 0 splňující f(r) = 0 a x 0 splňující 0 (x 0 /r) 1 1. Potom pro spočítání odhadu kořene s absolutní chybou menší p než 1 potřebuje Newtonova metoda O(log( log n )) iterací, za předpokladu, že p jako počáteční odhad kořene bylo použito x 0. Důkaz lemmatu je uveden v [1]. Věta 6.1.1. (Složitost algoritmu A s binárním vyhledáváním a klasickou aritmetikou) Buď n přirozené číslo. Pak algoritmus A, je-li použito pouze binární vyhledávání, rozhodne, zda n je nebo není perfektní mocnina v čase je-li použita klasická aritmetika. O(log 3 n log log log n) 32
Věta 6.1.2. (Složitost algoritmu A s binárním vyhledáváním a Karatsubovým algoritmem) Buď n přirozené číslo. Pak algoritmus A, je-li použito pouze binární vyhledávání, rozhodne, zda n je nebo není perfektní mocnina v čase O(log 2,585 n log log n log log log n) je-li použit pro násobení Karatsubův algoritmus. Důkaz. V algoritmu A je pro spočítání p-té odmocniny z n zapotřebí O( log n ) p iterací binárního vyhledávání. V každé iteraci je počítána aproximace p-té odmocniny, která je v zápětí umocněna na p-tou. Umocňování má složitost O(log p log α n). Pro pevný exponent p je tedy na spočítání n 1/p třeba čas O( logα n log nlog p ). Součet přes všechny prvočíselné exponenty p log n p získáme pomocí rovnosti 6.1: = O ( p log n log 1+α n log p p log 1+α n log log n p log n = log 1+α n p log n log p p ) 1 = O(log 1+α n log log n log log log n) p V našem případě předpokládáme použití Karatsubova algoritmu, tedy výsledná složitost algoritmu A pouze s binárním vyhledáváním je O(log 2,585 n log log n log log log n) = Věta 6.1.3. (Složitost algoritmu A s upravenou Newtonovou metodou a klasickou aritmetikou) Buď n přirozené číslo. Pak algoritmus A, je-li použita upravená Newtonova metoda, rozhodne, zda n je nebo není perfektní mocnina v čase pokud je použita klasická aritmetika. O(log 3 n) 33
Věta 6.1.4. (Složitost algoritmu A s upravenou Newtonovou metodou a Karatsubovým algoritmem) Buď n přirozené číslo. Pak algoritmus A, je-li použita upravená Newtonova metoda, rozhodne, zda n je nebo není perfektní mocnina v čase O(log 2,585 n log log n) je-li použit pro násobení Karatsubův algoritmus. Důkaz. V algoritmu A s upravenou Newtonovou metodou je provedeno pro každé prvočíslo p log n pouze log p iterací binárního vyhledávání a O(log( log n )) p = O(log log n log p) iterací Newtonovy metody, což plyne z lemmatu 6.1.1. Celkem je tedy pro jedno prvočíslo p log n provedeno O(log log n) iterací. Protože na každou iteraci je třeba čas O(log p log α n), z rovnice 6.1 a z Čebyševovy věty plyne výsledná složitost: log α n log log n log p = log α n log log n log p = p log n p log n = O(log α n log log n log n) = O(log 1+α n log log n) Dosadíme-li opět za α exponent Karatsubova násobení, vyjde nám: O(log 2,585 n log log n) 6.1.2 Výsledky měření Pro obě varianty algoritmu A byla náhodně generována čísla o velikosti 1 000, 2 000,..., 10 000 decimálních cifer. Pro každou velikost bylo vygenerováno 10 čísel. Vidíme, že algoritmy nejsou příliš efektivní, protože zpracovat 10 čísel jim trvalo poměrně dlouho (u 10 000 ciferných čísel algoritmu A s upravenou Newtonovou metodou 125,1 s, u algoritmu A s binárním vyhledáváním dokonce 245,7 s). Neefektivnost algoritmů A je ještě markantnější při porovnání s časy implementovaných algoritmů B a C. Pro informaci ještě uveďme, že zpracování 1 000 čísel o 500 cifrách trvalo oběma algoritmům A mezi 10 a 15 s. Algoritmus A s upravenou Newtonovou metodou byl o něco rychlejší (kolem 11 s) než algoritmus A s binárním vyhledáváním (kolem 13 s). Na grafech 6.1 a 6.2 tedy vidíme naměřené časy běhu programů, jimi proloženou funkci a rovnici této funkce. Pro oba algoritmy vidíme, že výsledný 34