Artykuł o algorytmie Euklidesa z portalu wrocławskiego

Transkrypt

Artykuł o algorytmie Euklidesa z portalu wrocławskiego
Algorytm Euklidesa
Czym jest Algorytm Euklidesa i jak taki algorytm można zapisać? Do czego służy? Jeżeli
chcesz poznać odpowiedź na te pytania, zapraszamy do lektury! Ten algorytm to rzecz
niezbędna dla każdego, kto chce wykorzystać w swoich programach siłę liczb.
Dwie monety - jeden problem
Nim przejdziemy do sedna sprawy, spróbujmy rozwiązać takie zadanie:
Dysponujemy monetami o dwóch nominałach - 36 i 60 złotych (to musi być w jakiś dawnych czasach...). Chcemy za
ich pomocą wypłacić tę samą kwotę - raz tylko monetami 36-cio złotowymi, raz tylko 60-cio złotowymi. Jaka jest
najmniejsza niezerowa kwota, która spełnia te wymagania?
No tak, to nie jest trudne - wystarczy chwilę pomyśleć, aby znaleźć tę kwotę - to 180.
Faktycznie:
180=36*5
180=60*3
Ale dlaczego 180 jest najmniejszą taką liczbą? Ten przykład był prosty, występowały w nim niewielkie liczby. Ale jak
znaleźć odpowiedź, gdy ktoś zapyta nas o monety o nominałach 2278 i 2814? Albo 1067 i 582? To wcale nietrudne!
Trochę matematyki
Na początek przypomnijmy kilka wiadomości z matematyki:
1. Mówimy, że liczba d dzieli liczbę n, jeśli istnieje taka liczba całkowita k, że n=k*d.
Np. 3 dzieli 12, gdyż 12=3*4, a 16 dzieli 144, gdyż 144=16*9.
Fakt, że d dzieli n zapisujemy w ten sposób: d | n. Mamy więc 3|12 oraz 16|144. Mówimy, że jeśli d dzieli n,
to d jest dzielnikiem n, a n jest wielokrotnością d.
Jeżeli liczba d nie dzieli n, będziemy pisać, że d ƚ n.
2. Jeżeli liczba dzieli zarówno liczbę , jak i liczbę , to dzieli też ich różnicę,
, oraz sumę
.
Linia powyżej mówi nam, że jeśli dzieli i dzieli , to znaczy, że obie te liczby można zapisać jako iloczyn i
odpowiednio oraz . W takim razie ich różnica,
, równa jest
, a to oznacza, że ta różnica jest
podzielna przez . Podobnie
jest podzielne przez , gdyż ta suma to
.
3. Jeżeli dzieli , i
dzieli , to dzieli .
4. Jeżeli liczby a i b nie mają żadnego wspólnego dzielnika, większego niż jeden (czyli nie istnieje liczba większa od
jeden, która dzieliłaby zarówno a jaki i b), to mówimy, że te liczby są względnie pierwsze. Zapisujemy to w ten
sposób :
.
Na przykład 18 i 35 są względnie pierwsze, za to 24 i 33 nie są względnie pierwsze, gdyż 3|24 oraz 3|33.
Najmniejsze i największe
Jaki mamy z tego pożytek? Powróćmy do przykładu z monetami o nominałach i . Szukaliśmy takiej kwoty, która
byłaby podzielna zarówno przez jak i przez . To oznacza, że taka kwota musi się dzielić przez te same liczby,
przez które dzielą się i . Chcemy też, aby ta liczba była jak najmniejsza.
Taką najmniejszą dodatnią liczbę, podzielną przez
oznaczamy przez
.
i przez , nazywamy najmniejszą wspólną wielokrotnością i
Jak ją wyznaczyć? Zauważmy, że wystarczy, aby
dzieliła się przez wszystkie dzielniki i . Chcemy
otrzymać liczbę jak najmniejszą, więc nie ma sensu duplikować dzielników - jeżeli jakaś liczba dzieli zarówno jak
i , ale nie dzieli obu tych liczb, to nie ma sensu, aby
dzieliła się przez , wystarczy aby dzieliła się
przez . Stąd często
.
Gdybyśmy potrafili wyznaczyć największą taką liczbę, przez którą dzielą się obie liczby
wyznaczylibyśmy
... dlaczego?
i , to w łatwy sposób
Spójrzmy na nasz przykład ,
. Widzimy, że to największy wspólny dzielnik tych dwóch
liczb. W takim razie ich najmniejsza wspólna wielokrotność musi być równa
. Dlaczego?
Ponieważ
musi dzielić się przez (bo jest podzielne przez ), musi się też dzielić przez ( jest
podzielne przez ), oraz przez - największy wspólny dzielnik i .
I to wszystko - każda liczba która dzieli oraz dzieli , dzieli też ich
(największy wspólny dzielnik), czyli .
Gdyby było inaczej, powiedzmy, że istniałaby liczba pierwsza , taka, że
, nie
byłoby
- wszak
jest większe niż i też dzieli obie nasze liczby.
Wiemy też, że
. Dlaczego?
Wobec tego liczby
oraz
są względnie pierwsze - w
znajdują się wszystkie wspólne
dzielniki i - gdy podzielimy i przez ich
, dostaniemy liczby, które nie mogą mieć wspólnego
dzielnika, większego od .
Wobec tego, wymnażając
przez i przez .
Faktycznie,
,
oraz
otrzymamy najmniejszą liczbę podzielną
.
Znajdowanie największego wspólnego dzielnika
Wiemy już, że pozostało nam znaleźć sposób na obliczanie największego wspólnego dzielnika.
Wiemy już pewną bardzo ważną rzecz - jeśli jakaś liczba
szczególności dotyczy to
. Mamy więc:
Jeśli
dzieli i dzieli , to dzieli również ich różnicę,
.W
to
Dlaczego? Wiemy już, że jeśli jakaś liczba dzieli i , to dzieli ich różnicę. Więc na pewno
wiemy też, że każdy wspólny dzielnik dwóch liczb dzieli ich sumę, stąd
tego zachodzi powyższa równość, bo każdy wspólny dzielnik i dzieli
dzielnik
i dzieli .
,
i
. Z kolei
. Wobec
wspólny
każdy
Stąd już tylko parę kroków do zachwycającego prostotą algorytmu Euklidesa.
Prześledźmy wykorzystanie faktu
dla konkretnych liczb, na przykład
i
:
Uff... trochę to przydługawe, ale w końcu dobrnęliśmy do rozwiązania musi być równe
,
gdyż dzieli się przez każdą niezerową liczbę, więc też przez
, które z kolei jest największym dzielnikiem
nie?
, czyż
To, co wykonaliśmy powyżej, to właśnie Algorytm Euklidesa. Dopóki jedna z liczb i nie jest równa , odejmujemy
mniejszą liczbę od większej. Każde kolejne
jest równe poprzedniemu, a kiedyś musimy dobrnąć do zera
(dlaczego?) i odnaleźć rozwiązanie, gdyż jeśli
, to
.
Jeszcze lepiej
Oczywiście da się ten algorytm ulepszyć. Zauważ, że trochę bez sensu było liczenie
kolejno
,
. Cały czas
odejmowaliśmy
, aż osiągnęliśmy liczbę mniejszą niż
. Tak przecież działa Algorytm Euklidesa - dopóki
pierwsza liczba jest większa od drugiej, odejmujemy drugą od pierwszej.
W takim razie nietrudno jest ulepszyć nasz algorytm do takiej postaci:
Wyjaśnijmy sobie to!
to wynik dzielenia przez bez reszty - mówiąc nieformalnie, jest to ilość pełnych
wystąpień w . Innymi słowy, tyle właśnie razy możemy odejmować od , aż dostaniemy liczbę mniejszą od . W
takim razie "kompresujemy" sobie kolejne kroki algorytmu, polegające na żmudnym odejmowaniu wielokrotnie, do
jedengo kroku. Zauważ, że
to właśnie wynik odjęcia od wszystkich "mieszczących" się w wystąpień .
Zauważ, jak przyspiesza to działanie algorytmu - gdybyśmy chcieli poprzednią metodą liczyć
...
Tymczasem w naszej nowej metodzie
mamy
.
Zapiszmy nasz algorytm w języku C++:
int NWD(int a, int b){
int q, temp;
while(b){
q = a/b;
temp = b;
b = a - q*b;
a = temp;
}
return a;
}
Jest to dokładnie zapis naszego algorytmu - dopóki mniejszy z elementów pary
"zamianę"
na
. Gdy
, nasz największy wspólny dzielnik to
nie jest zerem, wykonujemy
.
Jak szybko działa ten algorytm? Jeżeli znane są Ci pojęcia złożoności obliczeniowej to zapewne ucieszy Cię
wiadomość, że Algorytm Euklidesa działa w czasie logarytmicznym względem wielkości liczb i . Jeśli zaś nie, to
zachęcamy do dowiedzenia się czegoś na ten temat - nie mniej jednak oznacza to, że algorytm działa błyskawicznie.
Zachęcamy do zapisania tego algorytmu i wykorzystania go do rozwiązania zadania Monety. Jego dokładna treść i
specyfikacja znajdują się na ostatniej stronie artykułu.
Kolejny problem
Zastanówmy się nad rozwiązaniem poniższego zadania:
Aptekarz ma przed sobą ciężkie zadanie - musi odmierzyć gramów
leku, dysponuje dwoma odważnikami, o wagach i gramów. Może
użyć dowolnej ilości każdego z tych odważników na wadze szalkowej.
Znajdź sposób odważenia żądanej ilości leku.
Przykładowe odważenie jednego grama niebieskiego leku za pomocą
odważników o wagach i gramów.
Jak się do tego zabrać? Postarajmy się zapisać to zadanie w języku matematyki:
Szukamy takich liczb całkowitych i , że:
Zauważ, że nie ma sensu kłaść odważników tej samej wagi na różnych szalach - równoważyłyby się. Wobec tego
możemy założyć, że układamy odważniki wagi na lewej szalce, a odważniki wagi na prawej szalce. Wtedy różnica
wag na obu szalkach musi być równa albo , albo
- oznacza to, że jeśli umieścimy lek odpowiednio na prawej
bądź lewej szalce, waga będzie w stanie równowagi i odważymy szukane gramów.
Zauważmy,
również
oznaczeniu
to biorąc
że
musi
zachodzić
.
.
i
Jeśli
więc
W istocie,
umielibyśmy
skoro
znaleźć
takie
i
liczby
i
,
że
, to
(przy
dostalibyśmy
Okazuje się, że Algorytm Euklidesa potrafi znaleźć takie liczby.
Jeśli udałoby się znaleźć szukane liczby i , to byłoby kombinacją liniową liczb i - to właśnie oznacza
równanie
- liczbę d można zapisać jako sumę pewnej wielokrotności całkowitej liczby i pewnej
wielokrotności całkowitej liczby (nie przejmujmy się tym, że mamy minus - można by bez żadnej szkody
napisać
.
Zauważmy, że jeśli jakieś liczba
kombinacją liniową i . Spójrzmy:
i
są kombinacjami liniowymi i , to ich różnica,
Jeśli
dla pewnych całkowitych
, to
, również jest
.
Jak działa Algorytm Euklidesa? Zaczynamy od
. Zarówno jak i są oczywiście kombinacjami
liniowymi i . W każdym kolejnym kroku algorytmu zamieniamy jedną z tych liczb na ich różnicę. Wobec tego cały
czas algorytm pracuje na dwóch liczbach, będących kombinacjami liniowymi i . W końcu docieramy
do
- wobec tego też jest kombinacją liniową i . Spójrzmy na przykład:
Niech
Udało się! Nie dość, że znaleźliśmy
, równe , to wiemy, że
. Zauważ, że
policzyliśmy też krok wcześniej w algorytmie, że
. Widzimy więc, że takie przedstawienie w
postaci kombinacji liniowej nie jest unikalne. Zajmiemy się tą sprawą w dalszej części algorytmu, teraz postarajmy się
wyprowadzić sposób na obliczanie współczynników i do równania
. Przy okazji warto byłoby też
przekształcić powyższy przykład do ulepszonego algorytmu Euklidesa - tego, w którym zamiast odejmować liczby od
siebie wielokrotnie, od razu "wycinaliśmy" wszystkie wystąpienia mniejszej liczby z większej liczby.
Nie będzie to dużo trudniejsze - spójrzmy na ten sam przykład:
Niech
Poniżej przykład dla
i
:
Algorytm
O ileż szybciej! Wprowadźmy pewne oznaczenia, aby móc łatwo zapisać tę zależność algorytmem:
1. Oznaczmy kolejne liczby pojawiające się w Algorytmie Euklidesa, jako
,
,
i tak dalej...
2. Niech ciągi
spełniają zależności:
. Mamy na myśli to, że
Co to oznacza? Liczby i to współczynniki, które pozwalają przedstawić kolejne liczby pojawiające się w
algorytmie Euklidesa jako kombinacje liniowe i . Mamy więc dla naszego powyższego przykładu:
- Koniec algorytmu
Widzimy wobec tego, że gdy algorytm się zakończy ( dla pewnego
to
i
- one spełniają
to poprzednio uzyskana liczba
) to współczynniki, których szukamy,
gdyż wiemy, że gdy osiągamy zero dla
jest największym wspólnym dzielnikiem i .
Jak obliczać te współczynniki? Spójrzmy poniżej:
Więc jeśli przyjmiemy:
to otrzymamy:
Hurra!
Przyjrzyjmy się poniższemu algorytmowi:
int a[1000], s[1000], t[1000];
int A, B, n, q, temp;
cin >> A >> B;
a[0] =
a[1] =
s[0] =
t[0] =
n = 1;
A;
B;
1; s[1] = 0;
0; t[1] = 1;
while(a[n] != 0){
q = a[n-1]/a[n];
a[n+1] = a[n-1] - q*a[n];
s[n+1] = s[n-1] - q*s[n];
t[n+1] = t[n-1] - q*t[n];
n++;
}
cout << "NWD(" << A << "," << B << ") = " << a[n-1] << endl;
cout << s[n-1] << "*" << A << " + ";
cout << t[n-1] << "*" << B << " = " << a[n-1] << endl;
,
Wypróbuj go! Zastanów się, dlaczego tablice o rozmiarze
na pewno wystarczą. Zastanów się też, czy nie dałoby
się zapisać tego algorytmu z mniejszym zużyciem pamięci
- nie deklarując tablic (zwróć
uwagę na to, że np. gdy już obliczysz
i
, to nigdy nie będziesz korzystać z ,
,
.
Kilka ciekawych problemów
W jednym z naszych przykładów okazało się, że
sposobów. Jak wyznaczyć wszystkie takie sposoby?
można zapisać jako kombinację liniową i na wiele
Załóżmy, że:
Weźmy
,
dla liczby całkowitej .
Wtedy
Wobec tego dla każdego całkowitego i też są dobrymi współczynnikami, pozwalającymi uzyskać jako
kombinację liniową i . Znaleźliśmy nieskończenie wiele takich liczb. Ale czy to wszystkie takie liczby?
Załóżmy przeciwnie - że można znaleźć takie dwie liczby całkowite
,
, że nie różnią się od naszych i o
odpowiednio wielokrotność i . Mielibyśmy wtedy:
Ale i są względnie pierwsze (inaczej nie byłoby największym dzielnikiem). Wobec tego musi zachodzić:
A to oznacza, że muszą się różnić o wielokrotność odpowiednio i , co dowodzi, że nasz sposób znajduje wszystkie
rozwiązania.
Zadania
Zastanów się nad dwoma poniższymi problemami (możesz wysłać do nich rozwiązanie na sprawdzaczkę).
1. Mamy liczbę pierwszą , oraz liczbę całkowitą dodatnią ,
. Jak znaleźć taką liczbę całkowitą dodatnią
,
, taką, że
?. (Taką liczbę nazywamy odwrotnością w modulo p).
2. Rozważmy wspominany problem aptekarza, z jednym dodatkiem - aptekarz chciałby użyć jak najmniej
odważników. Jak znaleźć takie rozwiązanie?
Poniżej znajdują się dokładne specyfikacje tych zadań i okienka do wysłania rozwiązań. Zachęcamy do spróbowania
swoich sił!
Zadanie Monety
Limit czasowy: 1s; Limit pamięciowy: 16 MB
Aptekarz Joe bardzo lubi liczyć bilon z kasy w swojej aptece. Dzisiaj wpadł na pomysł, aby wziąć sobie dwie monety o
pewnych nominałach i znaleźć taką kwotę, którą można wypłacić zarówno za pomocą dowolnej ilości monet
pierwszego rodzaju jak i za pomocą dowolnej ilości monet drugiego rodzaju. Joe nie jest zbyt inteligentny, poprosił
więc Cię o pomoc. Napisz program, który znajdzie dla niego taką kwotę!
Wejście:
Pierwsza linia wejścia składa się z jednej liczby całkowitej
W kolejnych liniach znajdują się pary liczb całkowitych ,
, oznaczającej ilość testów do rozważenia.
.
Wyjście:
Dla każdej pary należy na wyjściu wypisać jedną linię z jedną liczbą - najmniejszą taką kwotą, którą można wypłacić
zarówno za pomocą nominału jak i .
Przykład:
Dla wejścia:
3
10 6
36 60
11
Poprawne wyjście to:
30
180
1
31.10.2009 - Damian Rusak

Podobne dokumenty