Szybkie potęgowanie modulo
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^90 ≤ b ≤ 10^181 ≤ 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 jest1 % m, a nie1.
Przykład
2 10 1000
24
$2^{10} = 1024$, a $1024 \bmod 1000 = 24$.
Potrzebujesz teorii?
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
foraniwhile, 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.
Przywrócono Twój zapisany kod.
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 uruchomiono3 0 7
1
Test 2
Nie uruchomiono5 3 1
0
Test 3
Nie uruchomiono0 5 7
0
Test 4
Nie uruchomiono7 13 1000
407
Test 5
Nie uruchomiono123456789 987654321 1000000000
974933589
Test 6
Nie uruchomiono2 1000000000000000000 1000000007
719476260
Test 7
Nie uruchomiono1000000000 999999999999999999 999999937
115968790