Szybkie potęgowanie modulo

Zadanie 12 z 13 · rozdział 15Trudność: 2 z 3rekurencjapotęgowaniemodulo

Treść zadania

Oblicz $a^b \bmod m$, czyli resztę z dzielenia $a^b$ przez $m$, dla wykładnika $b$ nawet rzędu $10^{18}$.

Napisz rekurencyjną funkcję potega_modulo(a, b, m), która połowi wykładnik:

  • $a^0 = 1$,
  • jeśli $b$ jest parzyste: $a^b = \left(a^{b/2}\right)^2$,
  • jeśli $b$ jest nieparzyste: $a^b = a \cdot \left(a^{(b-1)/2}\right)^2$,

a po każdym mnożeniu bierze resztę z dzielenia przez $m$.

Dane wejściowe

Jedna linia: trzy liczby całkowite a b m oddzielone spacjami.

Dane wyjściowe

Jedna liczba całkowita — wartość $a^b \bmod m$ (z zakresu 0 … m-1). Przyjmujemy $a^0 = 1$, także dla $a = 0$.

Ograniczenia

  • 0 ≤ a ≤ 10^9
  • 0 ≤ b ≤ 10^18
  • 1 ≤ m ≤ 10^9

Uwagi

  • Nie używaj pow(a, b, m), pow(a, b) ani operatora ** — potęgowanie ma wykonać Twoja funkcja.
  • W ZAD-03 wykładnik malał o 1, więc potrzeba było $b$ wywołań — dla $b = 10^{18}$ to niewykonalne (a głębokość rekurencji przekroczyłaby limit). Połowienie wykładnika daje tylko około $\log_2 b$ wywołań: dla $b = 10^{18}$ to około $60$.
  • Wywołaj funkcję dla połowy wykładnika raz, zapisz wynik w zmiennej i dopiero ją podnieś do kwadratu (x * x). Dwa osobne wywołania dla tej samej połowy znów dałyby około $b$ wywołań.
  • Reszta z iloczynu nie zmieni się, jeśli czynniki wcześniej zastąpimy ich resztami: $(x \cdot y) \bmod m = ((x \bmod m) \cdot (y \bmod m)) \bmod m$. Dzięki temu liczby w obliczeniach pozostają małe.
  • Pamiętaj o przypadku $m = 1$: każda liczba daje resztę 0, więc dla $b = 0$ wynikiem jest 1 % m, a nie 1.

Przykład

Wejście
2 10 1000
Wyjście
24

$2^{10} = 1024$, a $1024 \bmod 1000 = 24$.

Zasady obowiązujące w rozdziale 15

Poniższe zadania uczą rekurencji: funkcja rozwiązuje problem, wywołując samą siebie dla mniejszych danych, aż dojdzie do przypadku bazowego, w którym odpowiedź jest znana od razu (np. $0! = 1$).

Konwencje wspólne:

  • Każde zadanie to osobny program: czyta standardowe wejście i wypisuje wynik na standardowe wyjście. Program wczytuje dane, wywołuje funkcję opisaną w treści i wypisuje wynik (gotowy szkielet znajdziesz w sekcji „Kod startowy”).
  • Rozwiązanie musi być rekurencyjne. Właściwe obliczenia wykonuje funkcja, która wywołuje samą siebie. Nie używaj w niej pętli for ani while, ani gotowych narzędzi, które wykonają całą pracę za Ciebie (np. sum(), pow(), operatora **, math.factorial(), list.index()). Sprawdzarka porównuje tylko wynik, ale celem zadań jest ćwiczenie rekurencji.
  • Każda funkcja rekurencyjna potrzebuje przypadku bazowego (kiedy przestajemy się wywoływać) i kroku rekurencyjnego (wywołania dla mniejszego problemu). Bez przypadku bazowego program przerwie działanie błędem RecursionError.
  • Python ogranicza głębokość rekurencji (domyślnie do około 1000 zagnieżdżonych wywołań), dlatego dane w zadaniach są małe.
  • Liczby naturalne liczymy od zera: $0, 1, 2, \dots$
  • Program nie wypisuje komunikatów typu „Podaj liczbę:”.

Zadanie pochodzi z otwartego zbioru Nauka-Programowania (z rozwiązaniami wzorcowymi). Zgłoś błąd w treści lub testach.

Python uruchomi się w przeglądarce przy pierwszym teście.

Kod zapisuje się automatycznie w tej przeglądarce. Tab wstawia wcięcie; aby opuścić edytor klawiaturą, naciśnij Esc, a potem Tab.

Testy

Program dostaje „Wejście” przez input() i musi wypisać „Oczekiwane wyjście”. Liczby porównywane są z tolerancją 0,01, a tekst podany w input("…") nie jest sprawdzany.

Test 1

Nie uruchomiono
Wejście
3 0 7
Oczekiwane wyjście
1

Test 2

Nie uruchomiono
Wejście
5 3 1
Oczekiwane wyjście
0

Test 3

Nie uruchomiono
Wejście
0 5 7
Oczekiwane wyjście
0

Test 4

Nie uruchomiono
Wejście
7 13 1000
Oczekiwane wyjście
407

Test 5

Nie uruchomiono
Wejście
123456789 987654321 1000000000
Oczekiwane wyjście
974933589

Test 6

Nie uruchomiono
Wejście
2 1000000000000000000 1000000007
Oczekiwane wyjście
719476260

Test 7

Nie uruchomiono
Wejście
1000000000 999999999999999999 999999937
Oczekiwane wyjście
115968790
Uruchom z własnymi danymi