Podstawy Programowania
Złożoność obliczeniowa
Sumy prefiksowe
Zliczanie kubełkowe
Wyszukiwanie binarne
Skalowanie
Metoda gąsienicy
Arytmetyka modularna i szybkie potęgowanie
Czasem zdarza się, że niektóre wartości w naszym programie (np. wynik) są bardzo duże i nie mieszczą się w zakresach zmiennych udostępnianych przez języki programowania. Często jako wynik programu wystarczy wtedy podać jedynie jego resztę z dzielenia przez z góry określoną liczbę Zazwyczaj w celu ułatwienia obliczeń wybiera się będące liczbą pierwszą. Dzisiaj dowiesz się, jak wykonywać podstawowe operacje w arytmetyce modularnej oraz szybko potęgować liczby.
Poprzez zapis: (czytaj: przystaje do modulo ) rozumiemy, że liczby i mają tą samą resztę z dzielenia przez czyli że jest podzielne przez Na przykład: Takie "równanie" nazywamy kongruencją.
Trzy wyżej wymienione operacje wykonujemy dokładnie tak samo, jak w przypadku zwykłej arytmetyki na liczbach:
Na przykład:
Innymi słowy, jeśli chcemy poznać resztę z dzielenia przez sumy dwóch liczb, możemy najpierw zamiast każdej z nich wziąć jej resztę (mod ), dodać do siebie te reszty i wziąć resztę z dzielenia przez tej sumy. Otrzymamy wtedy taki sam wynik, jak gdybyśmy najpierw dodali do siebie obie liczby, a następnie policzyli resztę tej sumy modulo
W przypadku odejmowania istnieje niebezpieczeństwo, że wynik będzie ujemny. Chcielibyśmy jednak modulować jedynie liczby dodatnie. Dlatego do wyniku odejmowania dodajemy - nie zmienia to nam reszty z dzielenia przez danej liczby, a zapewnia, że liczba, której resztę z dzielenia następnie obliczymy, będzie dodatnia.
W przypadku mnożenia istnieje natomiast niebezpieczeństwo, że jeśli liczba jest duża (np. ) to mnożąc ze sobą dwie reszty, wykroczymy poza zakres intów. Aby uniknąć tego problemu, musimy po prostu pamiętać o zastosowaniu long longów.
Zauważmy, że:
Pozwala to na zaimplementowanie prostego algorytmu do szybkiego potęgowania liczb:
long long modulo=1000000009;
long long pow(long long base, long long power) {
if (power == 0)
return 1;
else if (power % 2 == 0) {
long long result = pow(base, power / 2);
return (result * result) % modulo;
}
else
return (base * pow(base, power - 1)) % modulo;
}
Ile razy wykona się ta funkcja? Wykładnik nie będzie nieparzysty więcej razy niż parzysty. Oznacza to, że w przynajmniej połowie przypadków zmniejsza się on dwukrotnie – liczba wywołań funkcji będzie rzędu Jest to istotna różnica w porównaniu do wykonywania mnożeń „na pałę forem”.
Niestety, dzielenia modulo nie możemy wykonywać tak "niefrasobliwie", jak reszty operacji, ponieważ bardzo często daje inną resztę z dzielenia przez niż
Np. z jednej strony a z drugiej: Coś tutaj się nie zgadza...
Może też się zdarzyć, że nie jest podzielne przez a mimo tego dzielenie modulo da się wykonać, np. nie dzieli , ale ponieważ }
Aby dzielenie modulo wykonywać poprawnie, musimy podejść do niego od całkiem nowej strony. Otóż przedstawmy sobie dzielenie jako mnożenie przez odwrotność:
Gdzie to taka liczba, że Umiemy już mnożyć, więc musimy nauczyć się znajdywać odwrotność modulo. W tym celu skorzystamy z Małego Twierdzenia Fermata, które mówi, że: Dla będącego liczbą pierwszą i niepodzielnego przez zachodzi:
Dowód: Rozważmy wszystkie możliwe kolorowania "koła fortuny", podzielonego na kawałków, na kolorów. W sumie jest ich - każdy kawałek możemy pokolorować na jeden z kolorów. Zauważmy, że po obróceniu dowolnego kolorowania, takiego, że nie całe koło jest pomalowane na ten sam kolor, otrzymamy jakieś inne kolorowanie. W sumie kolorowań, w których całe koło jest jednokolorowe, mamy - tyle, ile kolorów. Czyli pozostałe kolorowań możemy poustawiać po kolorowań tak, że kolorowania w jednej grupie da się uzyskać z siebie poprzez obracanie koła. Każde kolorowanie trafi do dokładnie jednej grupy wraz z kolorowaniami, które możemy z niego uzyskać, obracając koło. Czyli dzieli Ponieważ nie dzieli oraz jest pierwsze, musi dzielić czyli To kończy dowód.
Wykorzystując Małe Twierdzenie Fermata, dostajemy:
(pomnożyliśmy stronami poprzednią kongruencję przez )
(ponieważ "znikają" z obu stron)
umiemy policzyć w używając szybkiego potęgowania, czyli umiemy już też dzielić
Przypomnijmy na koniec, że aby zastosować Małe Twierdzenie Fermata, musi być liczbą pierwszą. Jak radzić sobie z dzieleniem, gdy jest ono złożone dowiesz się w artykule Teoria liczb I.