Największy wspólny dzielnik dwóch liczb naturalnych – ALGORYTM EUKLIDESA Algorytm opisany w Księdze VII „Elementów” Euklidesa z III wieku p.n.e. pozwala szybko znaleźć nwd(a,b) - największy wspólny dzielnik dwóch liczb naturalnych a i b. dla a = b a nwd (a, b) = nwd ( a − b, b ) dla a > b nwd (a, b − a ) dla a < b lub dopóki a róŜne od b jeŜeli a>b to a=a-b w przeciwnym razie b=b-a zwróć a Ten algorytm nie jest optymalny, zwłaszcza gdy a i b róŜnią się znacznie, np. a = 1010 i b = 10. A oto szybszy alogorytm, który zamiast odejmowania wykorzystuje operację dzielenia modulo : a dla b = 0 nwd (a, b) = nwd ( b, a mod b ) dla b >= 1 gdzie: mod – operator dzielenia modulo; wynikiem jego działania jest reszta z dzielenia a przez b, na przykład 19 mod 7 = 5 Przykład: nwd(16,12) = nwd(12,4) = nwd(4,0) = 4 - funkcja nwd jest wywoływana 3 razy: Napisz ciąg wywołań funkcji rekurencyjnej nwd(a,b) i podaj ile razy wywołano funkcję nwd : a) dla danych a=56 i b=72 Odp: 4 wywołania, nwd=8 b) dla danych a=76 i b=56 Odp: 5 wywołań, nwd=4 c) Podaj iteracyjny przepis funkcji nwd(a,b) z dzieleniem modulo: Odp: dopóki (b>0) r ← a mod b a ← b b ← r zwróć a Istnieje jeszcze szybsza wersja algorytmu Euklidesa: binary GCD, która zamiast zwykłych operacji dzielenia wykorzystuje przesunięcia bitowe przy dzieleniu i mnoŜeniu przez 2: 1. jeŜeli a i b są parzyste, to NWD(a, b) = 2*NWD(a/2, b/2) 2. dopóki b>0 powtarzaj • jeŜeli a jest parzysta i b jest nieparzysta, to NWD(a, b) = NWD(a/2, b) zaś jeŜeli a jest nieparzyste a b jest parzyste, to NWD(a, b) = NWD(a, b/2) • jeŜeli a i b są nieparzyste, to gdy a>b to NWD(a, b) = NWD((a−b)/2, b) zaś gdy a<b, to NWD(a, b) = NWD(a,((b-a)/2). 3. zwróć a*2k, gdzie k jest liczbą wspólnych czynników 2 wyeliminowanych w kroku 1. ROZSZERZONY ALGORYTM EUKLIDESA Największy wspólny dzielnik moŜna przedstawić w postaci zaleŜności liniowej od a i b: nwd(a,b) = x*a + y*b gdzie x i y są współczynnikami o wartościach całkowitych (niekoniecznie dodatnich) Przykład: nwd(45,35) = 5 = -3*45 + 4*35 nwd(45,35) = nwd(35,10) tu: x=-3 i y=4 = nwd(10,5) = nwd(5,0) = 5 ZauwaŜ: 45 = 1 reszty10 35 tzn. 10 = 45 − 1 * 35 oraz 35 = 3 reszty 5 10 tzn. 5 = 35 − 3 *10 Zbierzmy to razem od końca: 5 = 35 – 3*10 = 35 – 3*(45-1*35) = 35 – 3*45 + 3*35 = -3*45 + 4*35 Rozszerzony algorytm Euklidesa pozwala łatwo znaleźć nie tylko nwd(a,b), ale takŜe współczynniki x,y zaleŜności liniowej x*a+y*b dzieki temu, Ŝe przechowuje w kolejnych iteracjach nie tylko resztę z dzielenia: r = a mod b, ale takŜe wynik dzielenia całkowitego: iloraz q = a div b gdzie div – operator dzielenia cakowitego. Współczynniki x, y w i-tej iteracji Rozrzeszonego Algorytmu Euklidesa opisane są zaleŜnościami: xi = xi −2 − q * xi −1 yi = yi − 2 − q * yi −1 x, y przyjmują zawsze wartości początkowe (dla iteracji i=0): krok (-2): krok (-1): x− 2 = 1 y − 2 = 0 x−1 = 0 y−1 = 1 Ostateczne wartości x,y otrzymujemy w tym kroku iteracji, który zawiera ostatnią niezerową wartość reszty z dzielenia a mod b. Przykład dla a=45 i b=35 i a=45 b=35 r = a mod b -2 -1 0 - - 45 1 2 3 x = xi-2 – q*xi-1 y = yi-2 – q*yi-1 - q = a div b - 1 0 0 1 35 10 1 1 – 1*0 = 1 0 – 1*1 = -1 35 10 5 3 0 – 3*1 = -3 1 – 3*(-1) = 4 to jest wynik x to jest wynik y 10 5 5 0 - - - 0 - - - - to jest wynik nwd ZaleŜność liniowa: nwd(45,35) = ………..5…….. = …-3..…*45 + …….4….. * 35 nwd x a y b Zadanie: znajdź nwd(a,b) i jego zaleŜność liniową od a,b dla danych: a=20 i b=13 Uzupełnij zacienione komórki w tabeli i formułę znajdującą się poniŜej tabeli: a 20 b r = a mod b 13 0 tu wynik nwd q = a div b - x = xi-2 – q*xi-1 1 0 y = yi-2 – q*yi-1 0 1 tu jest wynik x tu jest wynik y 0 - - - - - - - Twoja zaleŜność liniowa: nwd(20,13) = ………..……….. = ……..…*20 + …….….. * 13 Odp: nwd(20,13) = 1 = 2*20 – 3*13 Zadanie : Napisz i uruchom program, który pobiera dwie liczby naturalne a,b a następnie oblicza i zwraca wartość nwd(a,b) oraz wartości współczynników x,y zaleŜności nwd(a,b)=x*a + y*b. czytaj a,b; x2 ← 1 x1 ← 0 y2 ← 0 y1 ← 1 dopóki (b>0) r ← a mod b q ← a div b jeŜeli r>0 x ← x2 – q*x1 y ← y2 – q*y1 x2 ← x1 x1 ← x y2 ← y1 y1 ← y a b pisz ← b ← r ”nwd=” a ”x=” x1 ”y=” y1 czerwoną czcionką napisano to, co stanowi rozszerzenie zwykłego algorytmu Euklidesa Zastosowania Rozszerzonego Algorytmu Euklidesa I. Lanie wody Równanie diofantyczne liniowe: x*a + y*b = k gdzie dane są liczby całkowite: a,b,k, jest to równanie, którego rozwiązania: x,y szuka się w dziedzinie liczb całkowitych. Rozwiązanie istnieje tylko wtedy, gdy największy wspólny dzielnik nwd(a,b) jest także dzielnikiem liczby k. Problem można interpretować następująco: Masz dużą kadź oraz dwa mniejsze naczynia o pojemnościach a i b. Aby umieścić w kadzi dokładnie k litrów wody za pomocą naczyń a i b,odmierzysz wodę x razy naczyniem o pojemności a, oraz y razy drugim naczyniem, o pojemności b. Kadź uda się napełnić żądaną objętością k litrów wody przy pomocy naczyń a,b tylko wówczas, gdy nwd(a,b) jest także podzielnikiem liczby k. Przykład: 1) dla danych: a=5, b=7, k=3 2) dla danych: a=4, b=10, k=15 istnieje rozwiązanie: x=2, y=-1 nie ma rozwiązania. Zwykły algorytm Euklidesa pomoże rozstrzygnąć problem: czy w ogóle uda się odmierzyć k litrów naczyniami a i b. Jeżeli tak – to rozszerzony algorytm Euklidesa dostarczy informacji o tym ile razy trzeba użyć naczynia a i ile razy użyć naczynia b, rozwiazaniem będą (współczynniki x,y). Ale często nie będzie to rozwiązanie optymalne z punktu widzenia praktycznego. Chciałoby się jak najmniej razy „machać” tymi naczyniami, to znaczy: aby liczba operacji nalewania i wylewania wody: |x|+|y| była minimalna. Przykład: odmierz 9 litrów przy pomocy naczyń: 12 i 21 litrowych. Rozszerzony algorytm Euklidesa zaproponuje: 6*12 + (-3)*21 = 9, razem 6+3=9 operacji ale przecież można prościej: 1*21 + (-1)*12 = 9 , wystarczą 2 operacje. Pozostaje także problem jak duża musi być kadź. Zauważ że jeden ze współczynników (x,y) może być ujemny, to znaczy musisz zabrać z kadzi a lub b litrów wody uprzednio nalanej. Kadź musi więc być odpowiednio duża, często większa od żądanej objętości k. Jaka jest potrzebna minimalna pojemność kadzi? Zadanie: W jaki sposób napełnić kadź dwoma litrami wody k=2, posługując się naczyniami o pojemnościach a=5 i b=7 litrów tak, aby w trakcie napełniania kadzi ilość zawartej w niej wody była jak najmniejsza? Jaką co najmniej pojemność musi mieć kadź? // program: napełnianie kadzi int A,B,k; // A,B – naczynia, k - kadź cout<<"Ile nalac ? "; cin>>k; cout<<"Jakie naczynia a,b ? "; cin>>A>>B; int a=A, b=B; int x, x1=0, x2=1; int y, y1=1, y2=0; int r,q; while (b>0) { r=a%b; q=a/b; if (r>0) { x=x2-q*x1; y=y2-q*y1; x2=x1; x1=x; y2=y1; y1=y; } a=b; b=r; } cout<<"nwd="<<a<<"=x*a+y*b="<<x<<"*"<<A<<" + "<<y<<"*"<<B <<endl<<endl; if (k%a!=0) cout<<"nie da się nalać "<<k<<" litrow tymi naczyniami\n"; else { cout<<"\n napelnic kadz mozna tak:\n"; int q=k/a; cout<<A<<"*"<<x*q<<" + "<<B<<"*"<<y*q<<endl; cout<<"\n ale moŜna i tak:\n"; int w=0; // bieŜący stan napełnienia kadzi while (w!=k) { // jeŜeli w kadzi jest za mało if (w<k) { if (k-w ==A) {w=w+A; cout<<" wlej "<<A<<" w="<<w<<endl;} else if (k-w ==B) {w=w+B; cout<<" wlej "<<B<<" w="<<w<<endl;} else { if (A>B) {w=w+A; cout<<" wlej "<<A<<" w="<<w<<endl;} else {w=w+B; cout<<" wlej "<<B<<" w="<<w<<endl;} } } else { // jeŜeli w kadzi jest za duŜo if (k-w ==-A) {w=w-A; cout<<" odlej "<<A<<" w="<<w<<endl;} else if (k-w ==-B) {w=w-B; cout<<" odlej "<<B<<" w="<<w<<endl;} else { if (A<B) {w=w-A; cout<<" odlej "<<A<<" w="<<w<<endl;} else {w=w-B; cout<<" odlej "<<B<<" w="<<w<<endl;} } } } } II. Rozszerzony Algorytm Euklidesa przydaje się w kryptografii z zastosowaniem algorytmu RSA, do wyznaczenia klucza prywatnego gdy znamy składniki klucza publicznego. Kodowanie RSA: szyfrowanie: e ( dana ) mod d deszyfrowanie: ( szyfr n ) mod n gdzie (n,e) – klucz publiczny gdzie (n,d) – klucz prywatny Najpierw wybiera się klucz publiczny (n,e) , jawny, służący do szyfrowania: Wybieramy dwie liczby pierwsze, na przykład: p = 7 i q = 11. Ich iloczyn: n = p*q = 77 Wyznaczamy Φ =(p -1)(q -1) = 6 *10 = 60. Wybieramy liczbę e względnie pierwszą z Φ , czyli taką że nwd(Φ ,e)=1, na przykład e = 13. Klucz publiczny: (77,13) Następnie wyznacza się klucz prywatny (n,d) , tajny, służący do odszyfrowania: Szukamy d takiego, Ŝe d*e ≡ 1 (mod Φ ) czyli w naszym przykładzie: d * 13 ≡ 1 (mod 60) Rozszerzony Algorytm Euklidesa NWD(Φ ,e) zwróci nam parę współczynników (x,y). Jeśli argumenty NWD są liczbami wzglednie pierwszymi, to (x,y) są są odwrotnościami modularnymi argumentów funkcji NWD(Φ,e) : Odwrotność modulo: x jest odwrotnością modulo b liczby a, NWD(Φ,e) = 1 = x*Φ + y*e to znaczy: (x*a) mod b =1, w innym zapisie x * a ≡ 1 (mod b) Liczby Φ , e są względem siebie pierwsze, więc y*e ≡ 1 (mod Φ) , co oznacza Ŝe znaleziona wartość y odpowiada d = y Φ 60 e 13 q=Φ’/e’ 4 1 1 1 1 2 Φ ’=e’ 60 13 8 5 3 2 1 e’=Φ ’ mod e’ 13 8 5 3 2 1 0 x-2 1 0 1 -1 2 -3 5 x-1 = xi-2–q*xi-1 0 1 -1 2 -3 5 y-2 0 1 -4 5 -9 14 -23 y-1= yi-2 – q*yi-1 1 -4 5 -9 14 -23 Sprawdźmy: nwd(Φ,e) = x*Φ + y*e = 5*60 + (-23)*13 = 1 Współczynnik y ma wartość ujemną. RównowaŜne mu będą wszystkie wartości róŜniące się o Φ=60, względem którego liczymy modulo: 13 * (-23) ≡ 1 mod 60 13 * (60 - 23) ≡ 1 mod 60 13 * 37 ≡ 1 mod 60 czyli d = 37. Klucz prywatny (77,37). Podanym kluczem moŜna szyfrować liczby o wartości mniejszej od n=77. NaleŜy utajnić wszelką informację o uŜytych liczbach p i q : 7 i 11, poniewaŜ znając te liczby łatwo moŜna wyznaczyć klucz prywatny. Cała trudność złamania szyfru RSA opiera się na trudności rozkładu liczby n na iloczyn liczb pierwszych p i q w realnym czasie (gdy wartość n jest dostatecznie duŜa) Zadanie : Dany jest klucz publiczny (e,n): n=91 oraz e=5. Wyznacz brakującą liczbę d w kluczu prywatnym: • wyznacz dwie liczby pierwsze : p i q takie Ŝe n=p*q Rozwiązanie - na przykład: p=13 i q=7) • dobierz liczbę d wiedząc Ŝe d jest odwrotnością modulo Φ liczby e, czyli d * e ≡ 1 (mod Φ ) Wskazówka: zastosuj Rozszerzony Algorytm Euklidesa dla nwd(Φ,e) i przypisz d← y Rozwiązanie: Φ=(p-1)*(q-1)= (13-1)*(7-1)=12*6=72 Φ - e - r = Φ mod e - q = Φ div e - y = yi-2 – q*yi-1 0 1 72 5 2 14 -14 5 2 1 2 2 1 0 - 29 to jest wynik d - Rozwiazanie: Klucz prywatny (d,n) = (29,91), wartość liczby deszyfrujacej d=29 Podczas szyfrowania RSA natrafiamy na problem bardzo dużych liczb jako wyników potęgowania. Można go uniknąć, bo potrzebna jest tylko potęga modulo n, co wcale nie musi być dużą liczbą. e h = a mod n można rozpisać jako h = ((a mod n) * a mod n) ... * a mod n I jeszcze usprawnić algorytm. Pamiętasz sprytny algorytm szybkiego potęgowania (dla wykładników naturalnych)? Wykorzystywał on właściwość b 2 a = (a ) 2 b funkcja potęga(a, b) // wersja rekurencyjna jeŜeli b = 0 zwróć 1 jeŜeli b jest nieparzysta zwróć a*potęga(a, n-1) w przeciwnym przypadku a = potęga(a, b/2) 2 zwróć a z potęgowaniem modulo jest podobnie: funkcja potega(a,b) // wersja iteracyjna: wynik=1; x=a; dopóki (b>0) jeŜeli (b nieparzyste) wynik *= x; b -w przeciwnym razie x *= x b = b/2 zwróć wynik funkcja potegaModulo (a, b, n ) // wersja iteracyjna wynik =1; dopóki (b>0) { jeŜeli (b nieparzyste) wynik = (wynik*a) mod n; a = (a*a) mod n; b = b/2; } zwróć wynik; } Zadanie Zastosuj klucz publiczny z poprzedniego zadania do zaszyfrowania słowa DEMOTYWATOR. Zaszyfruj osobno każdy znak tego słowa (biorąc jako daną źródłową kod ASCII znaku). Wyświetl (lub zapisz w pliku) ciąg liczb będących wynikiem szyfrowania. Nastepnie zdeszyfruj otrzymany ciąg liczb przy pomocy klucza prywatnego z poprzedniego zadania. Kolejne zdeszyfrowane liczby potraktuj jako kody ASCII i wyświetl ich reprezentację znakową. Mam nadzieję że otrzymasz z powrotem słowo DEMOTYWATOR int potegaModulo(int a, int b, int c) { int r=1; while (b>0) { if (b%2 != 0) r=(r*a)%c; a=(a*a)%c; b=b/2; } return r; } int main() { string s="DEMOTYWATOR"; int ns=s.length(); int n=77; int e=13; int d=37; // nie moŜesz szyfrować liczb większych od n // klucz publiczny (77,13) // klucz prywatny (77,37) int T[20]; // tablica przechowuje liczby po zaszyfrowaniu ponieważ najmniejszy kod (litera A) ma wartość 65, odejmuję 64 od wszystkich kodów aby uzyskać mniejsze liczby do szyfrowania for (int i=0; i<ns; i++) { int kod = (int)s[i]-64; // szyfrowanie: (kod^e) % n int zasz = potegaModulo(kod,e,n); cout<<s[i]<<" T[i]=zasz; "<<kod<<" "<<zasz<<" "<<endl; } cout<<endl<<endl; for (int i=0; i<ns; i++) { int z = T[i]; // deszyfrowanie: (z^d) % n int odsz = potegaModulo(z,d,n); char znak = (char)(odsz+64); cout<<znak<<" "<<odsz<<endl; } cout<<endl<<endl; system("PAUSE"); return EXIT_SUCCESS; }