Informatyczny kącik olimpijski
Kwadraty liczb naturalnych
W tym odcinku omówimy rozwiązanie zadania "Kwadraty liczb naturalnych", które pojawiło się na finale Zawodów Indywidualnych XIII Młodzieżowej Olimpiady Informatycznej.
Zadanie (Kwadraty liczb naturalnych). Piotr jest zafascynowany kwadratami liczb naturalnych, czyli liczbami: Chłopiec ma przed sobą ciąg
liczb naturalnych
Ile jest takich par liczb naturalnych w ciągu
że ich iloczyn jest kwadratem liczby naturalnej?
Niech oznacza wartość największej liczby w ciągu
zaś
niech oznacza największy iloczyn pary liczb w ciągu
Rozwiązanie
Zacznijmy od rozwiązania, w którym obliczamy iloczyn każdej z par elementów ciągu
Następnie zliczamy te iloczyny, które są kwadratami liczb naturalnych. Załóżmy, że chcemy sprawdzić, czy
jest kwadratem. W tym celu przeglądamy kwadraty kolejnych liczb naturalnych:
Jeśli trafimy na
to odpowiedź jest pozytywna. Jeśli trafimy na liczbę większą niż
wtedy przerywamy działanie i odpowiedź jest negatywna. Tym sposobem rozważymy
kwadratów. Zatem rozwiązanie działa w czasie
Rozwiązanie
Przyspieszmy sprawdzanie, czy jest kwadratem. Zaaplikujmy algorytm wyszukiwania binarnego na ciągu kwadratów:
Algorytmy wykona
operacji. Całe rozwiązanie działa w czasie
Szkic rozwiązania optymalnego
Zauważmy, że w rozkładzie na czynniki pierwsze kwadratu liczby naturalnej każdy czynnik pierwszy występuje parzyście wiele razy. Zatem iloczyn dwóch liczb jest kwadratem, jeśli zbiory czynników pierwszych występujących nieparzyście wiele razy w rozkładzie obu liczb są takie same. Niech więc gdzie
oznacza iloczyn czynników pierwszych, które występują nieparzyście wiele razy w rozkładzie
Innymi słowy,
to
podzielone przez swój największy dzielnik będący kwadratem. Wówczas
jest kwadratem, jeśli
Zatem wynikiem jest liczba par takich samych elementów w ciągu
Zliczanie par
Załóżmy przez chwilę, że znamy ciąg i chcemy policzyć liczbę par elementów o takich samych wartościach. W tym celu wystarczy posortować ciąg niemalejąco. Wówczas elementy o tych samych wartościach znajdą się obok siebie (będą tworzyły bloki). Następnie przeglądamy kolejne bloki elementów i zliczamy pary. W
-elementowym bloku można wybrać
par. Sortowanie realizujemy w czasie
podział na bloki odbywa się w czasie
Zatem zliczanie par tych samych elementów w ciągu
odbywa się w czasie
Wyznaczenie ciągu w
Wartość dla każdego
od
do
liczymy niezależnie. Początkowo niech
dla każdego
Następnie przeglądamy kolejne kwadraty
jako kandydatów na dzielniki
Jeśli rozpatrywany kwadrat jest dzielnikiem
to dzielimy
przez ten kwadrat. W ten sposób z rozkładu na czynniki pierwsze usuniemy kwadraty.
Wyznaczenie ciągu w
Zauważmy, że nie ma potrzeby przeglądania wszystkich kwadratów. Wystarczy rozważać kwadraty liczb pierwszych Pamiętajmy o tym, że kwadrat może wielokrotnie występować w rozkładzie na czynniki pierwsze. Wtedy musimy usunąć wszystkie jego wystąpienia. Przykładowo: liczbę
musimy dwukrotnie podzielić przez
Każda liczba zostanie podzielona co najwyżej
razy. Liczbę liczb pierwszych w przedziale
szacujemy jako
Stąd całkowita złożoność wynosi
Wyznaczenie ciągu w
Na początku zliczmy wystąpienia liczb w ciągu Niech
(zbiór indeksów elementów ciągu
o wartości
). Następnie przeglądamy kwadraty liczb pierwszych z przedziału
jako potencjalne dzielniki. Załóżmy, że rozpatrujemy kwadrat
Wiemy, że jest on dzielnikiem:
Dla każdej z tych wartości, korzystając z tablicy zliczającej
odwołujemy się bezpośrednio do elementów, które są podzielne przez
i te elementy dzielimy przez
Po zakończeniu otrzymane wartości tworzą ciąg
Do każdego elementu ciągu odwołamy się co najwyżej
razy. Liczba rozpatrywanych wielokrotności wynosi:
co w sumie wynosi
Zatem otrzymaliśmy rozwiązanie działające w czasie
Wyznaczenie ciągu w
Połączmy dwa poprzednie rozwiązania i
Najpierw, podobnie jak w
dla każdego elementu ciągu
rozważmy jego potencjalne dzielniki (kwadraty liczb pierwszych), nie większe niż
Takich dzielników jest
Następnie zaaplikujmy rozwiązanie
z drobną zmianą. Rozpatrujmy tylko wielokrotności kwadratów liczb pierwszych większych od
Niestety, tak opisane rozwiązanie nadal wymaga utworzenia tablicy zliczającej rozmiaru
Do implementacji tablicy zliczającej wystarczy użyć tablicy mieszającej (tablicy z haszowaniem) i problem zostaje rozwiązany. Otrzymujemy algorytm, który działa w czasie
Dowód tego oszacowania pozostawiam Czytelnikowi. Jednocześnie zachęcam do pomyślenia nad lepszym oszacowaniem.