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:
![]() |










