Cvičení Úloha 1: Rozhodněte zda posloupnost znaků v poli délky n tvoří palindrom (slovo, které je stejné při čtení zprava i zleva). Př.: [a,l,e,l,a] [a,n,n,a] [k,r,k] [i,d,o,l,z,l,o,d,i] Navrhněte vhodný algoritmus pro tuto úlohu Určete složitost navrženého algoritmu Navrhněte případně algoritmus rychlejší
Palindrom Algoritmus 1: Pro 1. prvek dělám n kroků, potom n-2, n-4, … 1. jestliže je seznam prázdný nebo jednoprvkový, potom skonči úspěšně 2. vezmi první prvek seznamu, smaž ho a jdi na konec seznamu ověřit, zda je tam tentýž prvek; jestliže ano také ho smaž, jinak konec 3. pokračuj krokem číslo 1 palindrom([]) :- !. palindrom([H]) :- !. palindrom([H|T]) :- kontrola(H, T, T1), !, palindrom(T1). kontrola(H, [H], []) :- !. kontrola(H, [C|T], [C|T1]) :- kontrola(H, T, T1). Pro 1. prvek dělám n kroků, potom n-2, n-4, … Složitost je tedy: f O(n2 )
Palindrom Algoritmus 2: 1. otoč seznam 2. postupně porovnávej prvky v původním a otočeném seznamu palindrom([]) :- !. palindrom2(Sez) :- reverse(Sez, S1), kontroluj(Sez, S1), !. kontroluj([], []) :- !. kontroluj([H|T], [H|T1]) :- kontroluj(T, T1). Otočení proběhne v lineárním čase Kontrola proběhne také v lineárním čase Složitost je tedy: f O(n)
Palindrom Praktický rozdíl mezi použitím algoritmu 1 a 2 na seznamu o velikosti N: (čas je uveden ve formátu min:sec:ssec) N Algoritmus 1 O(n2) Algoritmus 2 O(n) 10000 00 : 06 : 59 00 : 00 : 00 25000 00 : 51 : 63 00 : 00 : 01 50000 09 : 08 : 63 00 : 00 : 02 100000 49 : 49 : 95 00 : 00 : 03
Cvičení 2 Úloha 2: V zadané posloupnosti reálných čísel délky N nalezněte úsek s maximálním součtem. Př.: 1,3,5,-8,4,-7,15,3,-12,-8,7,-1,9,4,-6,-14,3,5,4,-17,11 Navrhněte vhodný algoritmus pro tuto úlohu Určete složitost navrženého algoritmu Navrhněte případně algoritmus rychlejší
Posloupnost max:=0; Algoritmus 1: for i=1 to n do for j=i to n do pom:=0; for k=i to j do pom := pom+a[k]; if pom > max then max := pom; Zkoumá všechny možné začátky a konce úseků Mezi nimi provede součet prvků Tři do sebe vnořené cykly; složitost je: f O(n3)
Posloupnost max:=0; Algoritmus 2: for i=1 to n do pom:=0; for j=i to n do pom := pom+a[j]; if pom > max then max := pom; Pro každý začátek zkoušíme počítat součty všech úseků Zbavili jsme se jednoho cyklu Složitost je: f O(n2)
Posloupnost max:=0; pom:=0; Algoritmus 3: for i=1 to n do pom := pom+a[i]; if pom > max then max := pom else if pom < 0 then pom := 0 Úsek s maximálním součtem musí začínat kladným číslem; pokud při načítání klesneme pod nulu, je čas začít znovu Zbavili jsme se dalšího cyklu Složitost je: f O(n)
Cvičení 3 Úloha 3: V zadané posloupnosti reálných čísel P1 délky N1 nalezněte nejdelší úsek, který neobsahuje žádný prvek z druhé zadané posloupnosti P2 o délce N2. Př.: P1: 1,3,5,-8,4,-7,15,3,-12,-8,7,-1,9,4,-6,-14,3,5,4 P2: 3,11,4,25,-12,-1,5,41,17,-22,13 Navrhněte vhodný algoritmus pro tuto úlohu Určete složitost navrženého algoritmu Navrhněte případně algoritmus rychlejší
Dva seznamy Algoritmus 1: 1. procházej seznam P1, pro každý prvek ověř, zda je i v P2 a pokud ano, nahraď jej vhodnou značkou (N1*N2 kroků) 2. projdi seznam P1 a nalezni nejdelší úsek bez značky (N1 kroků) Celkem: N1*N2 + N1 ≈ N2 Složitost je: f O(n2) Jsou-li seznamy dlouhé, vyplatí se zvážit datovou strukturu seznamu P2, ve kterém často vyhledávám
Dva seznamy 1. Převeď seznam P2 na binární strom (N2*log2N2 kroků) Algoritmus 2: 1. Převeď seznam P2 na binární strom (N2*log2N2 kroků) 2. procházej seznam P1, pro každý prvek ověř, zda je i v P2 (strom) a pokud ano, nahraď jej vhodnou značkou (N1*log2N2 kroků) 2. projdi seznam P1 a nalezni nejdelší úsek bez značky (N1 kroků) Celkem: N2*log2N2 + N1*log2N2 + N1 ≤ 2*N*log2N + N ≈ N*log2N Složitost je: f O(n*log2n)
Složitost algoritmu Vybrané problémy: Při analýze složitosti jednotlivých algoritmů často narazíme na problém, jakým způsobem vzít v úvahu velikost vstupu. Co když počítáme s maticí – je velikost vstupu n nebo n*n? Co když počítáme s velkými čísly? Jelikož máme brát v úvahu délku vstupu, pro běžný integer budeme uvažovat délku vstupu 1, ale co když jde o mnohem větší čísla? Pozor na problémy s pseudopolynomickou složitostí!
Složitost algoritmu Příklad: Jakou složitost má algoritmus pro násobení matic o rozměru n*n? Algoritmus: Prvek v i-tém řádku a j-tém sloupci matice Z se rovná skalárnímu součinu i-tého řádku matice X a j-tého sloupce matice Y. Formálně: 𝑧 𝑖𝑗 = 𝑘=1 𝑛 𝑥 𝑖𝑘 𝑦 𝑘𝑗
Složitost algoritmu Jakou velikost vstupu máme uvažovat? Je obvyklé pracovat s parametrem n, i když je zřejmé, že na vstupu bude n*n čísel. Složitost: Každý z n*n prvků nové matice vznikne jako skalární součin dvou vektorů o velikosti n, k čemuž je třeba provést n násobení a n-1 sčítání. Tedy n+n-1=2n-1 pro každý prvek nové matice Celkem tedy n*n*(2n-1)=2n3-n2 Složitost algoritmu: f O(n3)
Složitost algoritmu Příklad: Jakou složitost má algoritmus pro součet matic o rozměru n*n? Algoritmus: Prvek v i-tém řádku a j-tém sloupci matice Z se rovná součtu odpovídajících prvků matice X a matice Y. Formálně: 𝑧 𝑖𝑗 = 𝑥 𝑖𝑗 + 𝑦 𝑖𝑗 Složitost: Každý z n*n prvků nové matice vznikne jako součet dvou prvků původních matic, k čemuž je třeba provést 1 sčítání. Celkem tedy n*n*(1)=n2 Složitost algoritmu: f O(n2)
Složitost algoritmu Příklad: Jakou složitost má algoritmus pro součet dvou čísel? Algoritmus: Součet se provede jedinou instrukcí. Složitost: Není zde žádný cyklus, takže pokud jde o „standardní“ čísla, jediná instrukce mi vrátí součet. Složitost algoritmu: f O(1) Co když ale půjde o hodně velká čísla?
Složitost algoritmu Příklad: Jakou složitost má algoritmus pro součet dvou „hodně dlouhých“ čísel? Algoritmus: Součet se provede v cyklu tak, že postupujeme zprava číslici po číslici (a pracujeme s eventuálním přenosem). Složitost: Délka cyklu, ve kterém se provádí sčítání, je úměrná počtu číslic (délce vstupu) a vstup nemůžeme považovat za jedno číslo C. Složitost algoritmu: f O(N), kde N je délka vstupu (N = logC+1).
Složitost algoritmu Příklad: Jakou složitost má algoritmus pro faktorizaci (rozklad) čísla? Algoritmus: Číslo C zkouším postupně dělit čísly 2, 3, .., C-1 (lepší řešení je zkoušet to jen do 𝐶 ). Složitost: Tento algoritmus potřebuje pro číslo C celkem C-2 dělení, což může svádět k domněnce, že půjde o lineární složitost. Pro číslo 999 budeme potřebovat 997 dělení (nebo jen 31, protože do 999 =31,6).
Složitost algoritmu Pro číslo 231-1 = 2 147 483 647 (Mersennovo prvočíslo), které v paměti počítače zabere jen 31 bitů, budeme už ale potřebovat 2 miliardy dělení (popř. 231−1 =46340,9). Je tedy zřejmé, že o lineární složitost nepůjde. Složitost: Číslo C potřebuje k uložení zhruba N=log2C bitů a počet provedených dělení je vzhledem k délce vstupu úměrný 2N, (v případě, že postupujeme pouze do odmocniny, potom je to 2N/2, což je ale stejně exponenciální závislost). Složitost algoritmu: f O(2N).
Složitost algoritmu Právě uvedený příklad algoritmu patří mezi takzvané problémy s pseudopolynomickou složitostí. Pseudopolynomická složitost se nazývá složitost, která se vzhledem k číselné hodnotě vstupu jeví jako polynomická, ale fakticky se vzhledem k délce vstupu jedná o složitost exponenciální. Pro problém faktorizace není znám algoritmus pracující v polynomiálně omezeném čase, a právě na tohoto faktu využívají moderní šifrovací algoritmy.
Šifrovací algoritmy Při utajené výměně informací je možné postupovat takto: Pokud chce Karel poslat Evě utajený balík, zamkne ho svým zámkem. Eva balík dostane, přidá na něj svůj zámek a pošle zpět. Karel odstraní svůj zámek a pošle Evě už jen se zámkem Evy. Eva odemkne svým klíčem a konečně ví, co je v balíku. To znamená opakované posílání, je to zdlouhavé, a proto potřebujeme lepší systém distribuce/výměny klíčů.
Šifrovací algoritmy Řešením může být asymetrické šifrování, kdy každá osoba má dva klíče (jeden šifrovací, druhý dešifrovací). Šifrovací klíč je „veřejný“, dešifrovací je „soukromý“. Zpráva je zašifrována „veřejným“ klíčem. Přečíst ji potom lze s použitím „soukromého“ klíče. Je třeba mít vhodnou matematickou funkci, kterou lze (při znalosti určitých faktů) snadno invertovat.
Šifrovací algoritmus RSA RSA (R. Rivest, A. Shamir, L. Adlemann) Vygenerujeme dvě dostatečně velká prvočísla p a q (každé má délku 1024 bitů) a spočteme n=pq. Číslo n je parametrem šifrovacího systému (šifrovací modul) a zveřejňuje se spolu s veřejným klíčem. Spočítá se Eulerova funkce (n)= (p-1)(q-1) udává počet přirozených čísel menších než n, která jsou nesoudělná s číslem n (protože n je součinem dvou prvočísel, je takových čísel právě (p-1)(q-1))
Šifrovací algoritmus RSA Zvolíme privátní klíč e z množiny {1,..,n} nesoudělný s číslem (p-1)(q-1) K privátnímu klíči e vypočteme Eulerovým algoritmem veřejný klíč d podle vztahu de mod (p-1)(q-1)= de mod (n)= 1 Tato rovnice má jediné řešení d (1<d<(n)) Zakódování zprávy z se provede dle vztahu ze mod n =s Dekódování zprávy se provede dle vztahu sd mod n =z Korektnost šifry je dána vztahem: z=sd mod n = zde mod n = zk(n)+1 mod n =1z mod n=z
Šifrovací algoritmus RSA Jednoduchý příklad: Zvolíme číslem prvočísla p=11 a q=19 Spočteme šifrovací modul n=p*q=11*19= 209 Spočítáme Eulerovu funkci (n)= (p-1)(q-1) =10*18 = 180 Zvolíme privátní klíč e z množiny {1,..,n} nesoudělný s číslem (p-1)(q-1); nechť je to e=7 Veřejný klíč d vypočteme (rozšířeným Euklidovým algoritmem) ze vztahu de mod (p-1)(q-1)= de mod (n)= 1 d7 mod 180 = 1 z čehož dostaneme d=103
Šifrovací algoritmus RSA Rozšířený Euklidův algoritmus: Snadný výpočet pomocí tabulky 180 1 7 25 5 -25 180 div 7 = 25 180 mod 7 = 5 1-0*25 = 1 0-1*25 = -25
Šifrovací algoritmus RSA Rozšířený Euklidův algoritmus: Snadný výpočet pomocí tabulky 180 1 7 25 5 -25 2 -1 26 7 div 5 = 1 7 mod 5 = 2 0-1*1 = -1 1-(-25)*1 =26
Šifrovací algoritmus RSA Rozšířený Euklidův algoritmus: Výpočet končí ve chvíli, kdy je zbytek po dělení 1 Pokud vyjde kladné číslo, máme d, když číslo záporné (-77), musíme přičíst (n) a potom tedy d=180-77=103 Vidíme, že 103*7 mod 180 = 721 mod 180=1 180 1 7 25 5 -25 2 -1 26 3 -77 5 div 2 = 2 5 mod 2 = 1 1-(-1)*2 = 3 -25-(26)*2 =-77
Šifrovací algoritmus RSA Máme tedy veřejný klíč: n=209, e=7 a soukromý klíč n=209, d=103 Pošleme zprávu z=a, v ASCII je to 97 Zakódujeme použitím veřejného klíče e: 977 mod 209 =147 Dekódování zprávy se provede pomocí privátního klíče d: 147103 mod 209 = 97
Šifrovací algoritmus RSA Máme tedy Bezpečnost celého systému je založena na obtížnosti faktorizace přirozených čísel V našem případě (n=209) stačí dělit do 11 ( 209 =14,4) Jakmile mám p, q, potom pomocí e dopočtu d a mám to!!! V praxi: Používá se n o velikosti 1024 bitů a delší Text se rozdělí na bloky stejné délky a každé slovo se převede na jediné číslo, které se potom zašifruje Pokud by se šifrovalo každé písmeno zvlášť, bylo by možné kód prolomit statistickými metodami
Šifrovací algoritmus RSA Možné slabiny „algoritmu“: Malé n, které lze snadno faktorizovat „Náhodná“ prvočísla p, q nejsou náhodná (generátor!) Dvě blízká prvočísla – lze použít Fermatovu metodu: Položíme 𝐷= 𝑝−𝑞 2 Platí 𝑛= 𝑝+𝑞 2 2 + 𝑝−𝑞 2 2 a tedy 𝑛+ 𝐷 2 = 𝑝+𝑞 2 2 Nyní stačí zkoušet pro malá D, jestli je n+D2 čtverec Jakmile takové D naleznu, spočtu snadno 𝑝,𝑞= 𝑛+ 𝐷 2 ±𝐷