Liniowe sito
Jeśli potrzebne nam są do czegoś liczby pierwsze z pewnego początkowego zakresu, zazwyczaj wyznaczamy je za pomocą sita Eratostenesa...
Zaczynamy od wypisania kolejno wszystkich liczb od do Następnie zaznaczamy 2 i wykreślamy wszystkie jej wielokrotności, zaznaczamy 3 i wykreślamy jej wielokrotności, dalej to samo z kolejnymi niewykreślonymi liczbami: 5, 7 itd. Jest to bardzo efektywna metoda; wykonujemy w niej rzędu operacji, o czym można przekonać się, czytając artykuł pt. „Jak szybko działa sito?” w Delcie 4/2012. to prawie ale jednak nie. Większa niż liniowa złożoność czasowa związana jest z tym, że w sicie Eratostenesa pozwalamy sobie na pewną rozrzutność, gdyż niektóre liczby złożone wykreślamy wielokrotnie. Zależnie od szczegółów implementacyjnych, pierwszą taką liczbą złożoną jest 6 albo 12. Zastanówmy się jednak, czy nie dałoby się każdej liczby złożonej wykreślić dokładnie raz?
Tak jak w zwykłym sicie, na początku tworzymy listę wszystkich liczb od do Znów w pierwszym kroku ustalamy liczbę pierwszą Dalej będzie trochę inaczej niż poprzednio, ale nie tak znowu skomplikowanie. Rozważamy kolejne (niewykreślone i nie mniejsze niż ) liczby na liście i dla każdej z nich wykreślamy wszystkie liczby postaci dla
Zobaczmy to na przykładzie; niech
Na początku mamy i listę przeglądamy, począwszy od W pierwszym kroku wykreślamy liczby postaci dla czyli potęgi dwójki:
Przyszła pora na wykreślamy wszystkie liczby postaci :
Kolejną nieskreśloną liczbą jest Wykreślamy więc liczby postaci :
Nietrudno zgadnąć, co będzie dalej. Czytelnik zechce sprawdzić, że po pełnym rozpatrzeniu na liście pozostaną po prostu wszystkie liczby nieparzyste.
Przyszła wreszcie pora na Rozważamy wszystkie dotychczas nieskreślone wartości nie mniejsze niż Zaczynamy od czyli najpierw wykreślamy potęgi trójki:
Następnie mamy i wykreślamy tylko 15; i znika 21; dalej wykreślimy jeszcze 33 i 39:
W ten sposób rozważyliśmy przechodzimy do Za pomocą wykreślimy już tylko 25, a za pomocą wykreślimy 35.
Dla każdego kolejnego kandydata na liczba jest większa niż możemy więc śmiało stwierdzić, że na liście pozostały nam już tylko liczby pierwsze (i to wszystkie w badanym zakresie).
W tym przykładzie rzeczywiście każdą liczbę złożoną skreśliliśmy dokładnie raz. Nie jest to przypadek; liczbę złożoną
skreślamy w przebiegu algorytmu, w którym i (a jeśli to, oczywiście, dla ). To, w szczególności, oznacza, że dla każdej liczby złożonej obliczamy przy okazji jej najmniejszy dzielnik pierwszy.
Możemy więc pokusić się o stwierdzenie, że cała metoda wykonuje wymarzone operacji. Aby jednak dało się ją przekuć na algorytm o liniowym koszcie czasowym, musimy przyjrzeć się dokładniej używanej strukturze danych. Mamy tu do czynienia z dwoma typami operacji: znajdowaniem następnego elementu na liście (potrzebne do rozpatrywania kolejnych i ) oraz wykreśleniem danej liczby z listy. O ile pierwszą z tych operacji wykonuje się na liście standardowo w czasie stałym, o tyle druga z nich sprawia pewien kłopot. W przypadku listy nie mamy bowiem swobodnego dostępu do jej elementów. Taki dostęp daje np. tablica, która to struktura nie pozwala z kolei na proste usuwanie bądź wykreślanie elementów...
Klucz do rozwiązania tej ostatniej trudności stanowi połączenie dwóch wspomnianych struktur danych, czyli listy i tablicy. Dokładniej, oprócz dwukierunkowej listy wszystkich niewykreślonych elementów utrzymujemy tablicę wskaźników do poszczególnych liczb od 2 do na liście. Jeśli danej liczby nie ma już na liście, w odpowiednim polu tablicy możemy wstawić np. nil. Za pomocą wskaźników zapisanych w tablicy łatwo znajdujemy elementy listy, które chcemy wykreślić, a potem usuwamy je już standardowo – podpinając do siebie wzajemnie następny i poprzedni element listy.
Przykładowo, opisana struktura danych dla po wykreśleniu potęg dwójki wygląda tak:
Teraz możemy już z całą pewnością powiedzieć, że otrzymaliśmy liniową metodę wykrywania wszystkich liczb pierwszych w danym początkowym zakresie liczb.
Czy w praktyce jest ona lepsza od klasycznego sita Eratostenesa? Nie sądzę. Za to ma ona pewne ciekawe z teoretycznego punktu widzenia zastosowanie, o czym można przeczytać w kolejnym artykule w tym numerze Delty.