Przeskocz do treści

Delta mi!

Największy wspólny dzielnik

Tomasz Kociumaka

o artykule ...

  • Publikacja w Delcie: styczeń 2013
  • Publikacja elektroniczna: 01-01-2013
  • Autor: Tomasz Kociumaka
    Afiliacja: student, Wydział Matematyki, Informatyki i Mechaniki, Uniwersytet Warszawski
  • Wersja do druku [application/pdf]: (140 KB)

Podobnie jak wyznaczanie liczb pierwszych, problem znajdowania największego wspólnego dzielnika (NWD) dwóch liczb naturalnych ma klasyczne i zapewne wszystkim Czytelnikom znane rozwiązanie. Mowa oczywiście o algorytmie Euklidesa, jednym z najstarszych do dziś używanych algorytmów. Jest to rozwiązanie bardzo proste w implementacji, a w wersji z dzieleniem – efektywne: pozwala wyznaczyć NWD dwóch liczb nie większych niż math w czasie math W pewnych przypadkach istnieją jednak rozwiązania asymptotycznie szybsze.

Załóżmy, że chcemy wyznaczać największy wspólny dzielnik dla wielu par stosunkowo niewielkich liczb. A konkretnie – dla math par liczb całkowitych dodatnich nieprzekraczających math Stosując algorytm Euklidesa, otrzymujemy oczywiście algorytm o złożoności czasowej math Możemy także spamiętać w dużej tablicy odpowiedzi na wszystkie możliwe math zapytań. Wówczas przy pierwszym podejściu możemy otrzymać złożoność math  gdy dla każdej możliwej pary zastosujemy niezależnie algorytm Euklidesa. Bez problemów takie rozwiązanie możemy przyspieszyć do math  ponieważ pojedynczy krok algorytmu Euklidesa sprowadza wyznaczanie NWD dla pary math do problemu obliczenia NWD dla pewnej pary leksykograficznie mniejszej. Możemy zatem wypełniać tablicę w odpowiedniej kolejności i w każdym kroku przepisywać wynik ze wskazanej komórki. W praktyce lepiej w tej sytuacji korzystać z algorytmu Euklidesa w wersji z odejmowaniem, gdyż procesory wykonują je istotnie szybciej niż wyznaczanie reszty z dzielenia. Otrzymujemy w ten sposób następujący algorytm:

Algorytm  tablicuj-nwd(n)
  for i  = 1to n do
     nwd[i,i] = i;
  for i  = 1to n do

     for  j = 1toi −1 do
       nwd[j , i] = nwd[i, j] = nwd[ j,i − j];
Czas obliczeń wstępnych rzędu math  jest jednak mało satysfakcjonujący – dopiero przy math  zapytaniach podejście korzystające z algorytmu Euklidesa przestaje osiągać lepsze wyniki.

Spróbujmy odnieść pewne korzyści już z obliczeń wstępnych rzędu math Jak przekonaliśmy się w poprzednim artykule, w tym czasie można wyznaczyć wszystkie liczby pierwsze nieprzekraczające math Zaprezentowany tam algorytm jest jednak znacznie silniejszy – dla każdej liczby math oblicza jej najmniejszy dzielnik pierwszy (oznaczany tu przez math) oraz największą potęgę tego math dzielącą math Za ich pomocą można łatwo skonstruować algorytm znajdowania NWD, działający w czasie proporcjonalnym do liczby różnych dzielników pierwszych argumentów. Pesymistycznie wielkość ta wynosi math  więc zapytania obsługiwane są niewiele szybciej niż przez algorytm Euklidesa. Okazuje się jednak, że przy liniowych obliczeniach wstępnych można osiągnąć stały czas zapytania!

Na wstępie zauważmy, że jeśli jeden z argumentów jest liczbą pierwszą, obliczenie NWD jest bardzo łatwe. Również łatwo możemy wyznaczać NWD liczb mniejszych niż math: jeśli mamy do dyspozycji czas math na obliczenia wstępne, można spamiętać wszystkie wartości NWD, wywołując math  Jak się wkrótce przekonamy, dowolne zapytanie o NWD można sprowadzić do stałej liczby zapytań, w których każdy z argumentów jest liczbą pierwszą lub nie przekracza math Kluczowe jest tu pojęcie rozkładu specjalnego.

Definicja. Rozkładem specjalnym dodatniej liczby całkowitej math nazwiemy trójkę liczb całkowitych math dla której math oraz dla każdego math:

display-math

Przykładowo, jednym z rozkładów specjalnych liczby math jest math liczby math jest math a liczby math jest math

Następujący lemat stanowi podstawę indukcyjnego dowodu faktu, że każda dodatnia liczba całkowita ma rozkład specjalny. Ten dowód można natychmiast przekształcić w algorytm wyznaczający rozkłady specjalne wszystkich liczb od math do math w czasie liniowym na podstawie tablicy math

Lemat. Niech math będzie liczbą całkowitą, math oraz math Niech math będzie rozkładem specjalnym math dla którego math Wówczas math jest rozkładem specjalnym math

Dowód. Na początek zauważmy, że liczby math i  math są pierwsze lub nie większe niż math więc są pierwsze lub nie większe niż math Musimy wobec tego zająć się tylko liczbą math Jeśli math to oczywiście math jest liczbą pierwszą. Załóżmy więc, że math Ponieważ math jest dzielnikiem math a  math jest najmniejszym (różnym od math) dzielnikiem math więc math Wobec tego

display-math

a więc math co kończy dowód.


Pozostaje nam jeszcze wykorzystać rozkłady specjalne do efektywnego obliczania NWD. Tym razem najpierw przedstawimy algorytm, a potem zastanowimy się nad jego poprawnością. Przyjmiemy, że rozkłady specjalne zapamiętaliśmy w tablicy math

Algorytm  nwd(k, l)
  (x1,x2,x3) = rozklad[k];
  (y1,y2,y3) = rozklad[l];
  g  = 1;

  foreach i, j∈ {1,2,3}√ do
     if max(xi, y j)⩽  n then
       d = nwd[xi,yj ];
     else if x = y then d = x ;
             i    j         i
     elsed  = 1;
     g = g⋅d;
     xi  = xi/d;y j = y j/d;
  return g;

Na początek zauważmy, że w każdym obrocie pętli math jest pewnym wspólnym dzielnikiem math oraz math Co więcej, math jest największym wspólnym dzielnikiem math oraz math Jeśli math lub math nie mamy co do tego wątpliwości. Bez straty ogólności niech więc math oraz math Wówczas math jest liczbą pierwszą, a więc math  może być równe math lub math Drugą możliwość natychmiast wyklucza nierówność math

Pozostaje teraz wykazać, że math  Zrobimy to przez wskazanie niezmienników pętli:

  • math
  • math
  • math  dla już przetworzonych par math

Na ich podstawie wnioskujemy, że po ostatnim obrocie pętli math jest wspólnym dzielnikiem math i  math a liczby math i  math są względnie pierwsze.

W ten sposób skonstruowaliśmy algorytm, który na math zapytań o NWD liczb z zakresu math odpowiada w czasie math  O ile tylko math to ten algorytm jest najlepszy z tutaj przedstawionych. Okazuje się, że w pewien sposób można połączyć jego siły z algorytmem Euklidesa i otrzymać rozwiązanie, które nigdy nie jest asymptotycznie wolniejsze od żadnego z zaprezentowanych, a dla pewnych wartości math (np. math) jest niemal math razy szybsze. Stworzenie tego działającego w czasie math  algorytmu pozostawiamy Czytelnikowi jako zadanie.