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