Zapis wieżowy
Tym razem omówimy rozwiązanie zadania Zapis wieżowy z Akademickich Mistrzostw Polski w Programowaniu Zespołowym z 2006 roku.
Dla ciągu liczb naturalnych
oraz liczby naturalnej
należy wyznaczyć resztę z dzielenia przez
wieży potęgowej, w której liczby z ciągu są kolejnymi wykładnikami. Innymi słowy, mamy znaleźć wartość wyrażenia
![]() |
Przykładowo, gdyż
Oczywiście, bezpośrednie obliczanie potęg nie wchodzi w grę, gdyż ich wartości rosną coraz szybciej z każdym dodatkowym wykładnikiem i już liczba
ma 3951 cyfr. Dla uproszczenia zapisu wieżę potęgową będziemy oznaczali przez
W rozwiązaniu zadania pomoże nam twierdzenie Eulera, które mówi, że dla względnie pierwszych liczb naturalnych i
spełniona jest kongruencja
![]() |
(*) |
gdzie oznacza liczbę liczb względnie pierwszych z
i nie większych niż ta liczba. Jeśli zatem liczby
i
byłyby względnie pierwsze, to moglibyśmy:
- wyznaczyć
- rekurencyjnie obliczyć rozwiązanie mniejszego problemu dla ciągu
i modułu
czyli
- korzystając z twierdzenia Eulera, wyznaczyć rozwiązanie
![]() |
Przypomnijmy, że jeśli znamy rozkład modułu na czynniki pierwsze to do obliczenia
możemy wykorzystać następujący wzór:
![]() |
(**) |
Rozkład ten możemy znaleźć w czasie przeglądając wszystkie potencjalne dzielniki liczby
nie większe niż
Zatem w takim też czasie wykonamy krok 1 algorytmu. Z kolei potęgowanie
z kroku 3 możemy wykonać w czasie
stosując metodę wielokrotnego podnoszenia do kwadratu. W sumie wykonamy
wywołań rekurencyjnych, w
-tym wywołaniu obliczając
gdzie
oraz
dla
co da algorytm o złożoności czasowej

Zauważmy jednak, że ze wzoru widać, iż
dla nieparzystego
jest liczbą parzystą, natomiast dla parzystego
mamy
Wynika z tego, że ciąg kolejnych modułów
ma co najwyżej
wyrazów, zatem wystarczy, że wykonamy
wywołań rekurencyjnych. A po drugie, pracę wykonaną we wszystkich wywołaniach kroku 1 można sumarycznie oszacować przez
Zatem lepszym oszacowaniem czasu działania naszego algorytmu jest
czyli po prostu
Niestety, abyśmy mieli pewność, że algorytm działa poprawnie, nie tylko liczby i
muszą być względnie pierwsze, ale również liczby
i
dla wszystkich
Na szczęście twierdzenie Eulera można uogólnić, aby działało również bez założenia o względnej pierwszości. Nowa wersja brzmi następująco: dla dowolnych liczb naturalnych
i
spełniona jest kongruencja
![]() |
(***) |
gdzie jest pewną liczbą zależną od
jednak nie większą niż
Zanim udowodnimy to twierdzenie, zobaczmy, jak dzięki niemu naprawić nasz algorytm. Zmodyfikujemy go tak, aby nie tylko obliczał wartości ale również dodatkowy bit
równy 1 wtedy, gdy
Znowu pokażemy, jak wykonać krok (2) algorytmu: załóżmy zatem, że
i obliczyliśmy już
oraz
Wyznaczmy Jeśli
to
więc
i wystarczy przyjąć
Z kolei jeśli
to
czyli
![]() |
Zatem w obu przypadkach mamy Z kolei wyznaczyć
można następująco: jeśli
to
a w przeciwnym przypadku możemy wykonać potęgowanie
w każdej iteracji domnażając jedno
i sprawdzając, czy wynik osiągnął już
(wykonamy co najwyżej
takich iteracji). Ostatecznie złożoność czasowa całego algorytmu nie zmienia się.
Pozostaje udowodnić dane wzorem uogólnienie twierdzenia Eulera. Zdefiniujmy ciąg
następująco:
![]() |
oraz niech będzie najmniejszą liczbą, taką że
Oznaczmy też
Liczba jest liczbą powstałą po usunięciu z
wszystkich czynników pierwszych występujących w
zatem liczby
i
są względnie pierwsze. Z twierdzenia Eulera wynika zatem, że
![]() |
Ponadto każda z liczb jest całkowita, więc liczba
również. Mnożąc powyższe równanie przez
dostajemy
![]() |
Korzystając z faktu, że kongruencja jest spełniona wtedy i tylko wtedy, gdy spełniona jest
możemy przemnożyć przez
obie strony i moduł powyższego równania:
![]() |
Liczby i
są względnie pierwsze, zatem z multiplikatywności funkcji
dostajemy
Ponadto wykładniki w rozkładach na czynniki pierwsze liczb
zmniejszają się, więc
Zatem ostatecznie dostajemy tezę twierdzenia:
![]() |