Największy wspólny dzielnik dwóch liczb naturalnych – ALGORYTM

advertisement
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;
}
Download