- 23 kwietnia 2024
- Posted by: Daria
- Category: Baza wiedzy
Sito Eratostenesa jest jednym z najstarszych – zostało opisane w III w. p.n.e. i było używane już przez starożytnych Greków – oraz najskuteczniejszych algorytmów do znajdowania liczb pierwszych.
Co to jest Sito Eratostenesa?
Sito Eratostenesa służy do wyznaczania liczb pierwszych z zadanego przedziału, od 2 do n. Dla przypomnienia: liczba pierwsza to taka, która ma dokładnie dwa różne dzielniki: 1 oraz samą siebie. Z tego powodu wartość 1 nie jest liczbą pierwszą – najmniejszą taką liczbą jest 2.
Sito Eratostenesa to algorytm, którego autorem jest Eratostenes z Cyreny. Jest to postać znana z wyznaczenia obwodu Ziemi oraz oszacowania odległości Słońca i Księżyca od naszej planety.
Złożoność pamięciowa Sita Eratostenesa to O(n), czyli liniowa – co w przypadku dużych liczb może okazać się problemem, natomiast złożoność obliczeniowa to O(n * log log (n)). Choć jest to bardzo nietypowa złożoność, oznacza to, że nasz algorytm jest relatywnie szybki.
Kalkulator – Sito Eratostenesa
- Komunikat:
- Liczby pierwsze:
Jak działa Sito Eratostenesa?
Działanie algorytmu opiera się na zasadzie, która wynika bezpośrednio z definicji liczby pierwszej. Otóż w pierwszym kroku tworzymy tablicę, w której wszystkie wartości opisujemy jako „prawda” (każda z nich będzie odpowiadała jednej liczbie z naszego przedziału). W dalszej kolejności wybieramy najmniejszą liczbę pierwszą z tego przedziału – jest nią 2. Następnie, dla wartości kolejno 4, 6, 8… (czyli dla wszystkich wielokrotności tej liczby większych od niej samej) komórki ustawiamy na „fałsz”, po czym wybieramy kolejną liczbę z przedziału, dla której odpowiadająca komórka ma wartość „prawda”. Powtarzamy te kroki aż do momentu, w którym nie wykluczymy wielokrotności wszystkich liczb.
Zaczynając od liczby 2, oznaczanej jako pierwsza liczba pierwsza, algorytm eliminuje wszystkie jej wielokrotności. Następnie przechodzi do kolejnej nieoznaczonej liczby i powtarza proces.
Wartości, których komórki oznaczamy jako „prawda”, to liczby pierwsze. Zauważmy, że górną granicą pętli, która przechodzi po liczbach, jakich wielokrotności sprawdzamy, jest n.
Przeanalizujmy działanie tego algorytmu dla n = 16. Początkowo nasza tabela prezentuje się tak:
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Dla wszystkich podanych tutaj wartości zawartość tablicy jest równa „prawda”. Teraz zaznaczmy na czerwono wszystkie wielokrotności liczby 2 (większe od niej samej).
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Dalej powtórzmy ten proces dla liczby 3 (wielokrotności zaznaczmy tym razem na fioletowo).
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Następnie robimy tak samo z liczbą 5 (jej wielokrotności zaznaczając na niebiesko)…
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
…i liczbą 7 (zielony).
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Kolejno powinniśmy zaznaczać liczby 11 i 13, jednak ich wielokrotności są większe od 16 (czyli zakresu sprawdzanych wartości), więc nie doprowadzi to do żadnych zmian.
Liczby, które pozostały zaznaczone na czarno, to liczby pierwsze. Ostateczny wynik wygląda zatem tak:
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Z tego wynika, że liczby pierwsze znalezione w zakresie od 2 do 16 to:
2, 3, 5, 7, 11, 13
Sito Eratostenesa – Schemat blokowy i pseudokod
Poniżej znajdziesz Sito Eratostenesa w formie podstawowej, bez żadnych dodatkowych usprawnień. Okazuje się idealne, dla zrozumienia działania tego algorytmu.
Pseudokod – Sito Eratostenesa
1 2 3 4 5 6 7 8 9 10 11 12 | funkcja sitoEratostenesa(n) liczbyPierwsze[1..n] dla i = 1, 2, ... n wykonuj liczbyPierwsze[i] ← prawda liczbyPierwsze[1] ← fałsz dla i = 2, 3, ... n wykonuj jeżeli liczbyPierwsze[i] = prawda j ← i + i dopóki j ≤ n wykonuj liczbyPierwsze[j] ← fałsz j ← j + i zwróć liczbyPierwsze |
Opis Sita Eratostenesa – Pseudokod
- 1 linia – Deklaracja funkcji sitoEratostenesa, przyjmującej argument n.
- 2 linia – Tworzenie tablicy liczbyPierwsze o rozmiarze od 1 do n. Tablica ta będzie przechowywać informacje o liczbach pierwszych od 1 do liczby n.
- 3 linia – Pętla od 1 do n.
- 4 linia – Zmiana wszystkich elementów tablicy liczbyPierwsze jako prawda (true).
- 5 linia – Ustawienie wartości dla pierwszego elementu tablicy liczbyPierwsze jako fałsz (false), ponieważ 1 nie jest liczbą pierwszą.
- 6 linia – Druga pętla, iteracja od 2 do n.
- 7 linia – Sprawdzenie czy element tablicy o indeksie i jest oznaczony jako liczba pierwsza.
- 8 linia – Zmienna j równa się i + i, czyli pierwsza wielokrotność danej liczby. Pętla iteruje od i + i do n z krokiem i, oznaczając wszystkie wielokrotności i jako niebędące liczbami pierwszymi, ustawiając odpowiednie wartości w tablicy liczbyPierwsze na fałsz (false).
- 9 linia – Pętla z warunkiem sprawdzającym, czy zmienna j jest mniejsza lub równa n. Pozwala to sprawdzać wielokrotności, które nie przekraczają n (zakresu przeszukiwanych wartości)
- 10 linia – Przypisanie fałsz (false), do elementu tablicy odpowiadającemu wielokrotności liczby i.
- 11 linia – Dodanie do zmiennej j wartości i. Pozwala to zająć się kolejną wielokrotnością liczby.
- 12 linia – Zwrócenie tablicy liczbyPierwsze, która zawiera informacje o liczbach pierwszych od 1 do liczby n.
Sito Eratostenesa – Python, C++ i Java
Python – Sito Eratostenesa
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | def sitoEratostenesa(n): liczbyPierwsze = [True] * (n + 1) # Tworzenie tablicy z wartościami True liczbyPierwsze[0] = False # 0 nie jest liczbą pierwszą liczbyPierwsze[1] = False # 1 nie jest liczbą pierwszą for i in range(2, n + 1): if liczbyPierwsze[i]: j = i + i while j <= n: liczbyPierwsze[j] = False j += i return liczbyPierwsze # Wprowadzenie wartości n n = int(input("Podaj wartość n: ")) # Wywołanie funkcji sito_eratostenesa i wypisanie wyniku print(f"Liczby pierwsze od 0 do {n}:") tablica=sitoEratostenesa(n) for i in range(2, n + 1): if(tablica[i]): print(i) |
C++ – Sito Eratostenesa
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | #include <iostream> using namespace std; bool* sitoEratostenesa(int n) { bool* liczbyPierwsze = new bool[n + 1]; // Tworzenie tablicy dynamicznej for (int i = 0; i <= n; ++i) { liczbyPierwsze[i] = true; // Inicjalizacja wszystkich elementów jako true } liczbyPierwsze[0] = false; // 0 nie jest liczbą pierwszą liczbyPierwsze[1] = false; // 1 nie jest liczbą pierwszą for (int i = 2; i <= n; ++i) { if (liczbyPierwsze[i]) { int j = i + i; while (j <= n) { liczbyPierwsze[j] = false; j += i; } } } return liczbyPierwsze; } int main() { int n; cout << "Podaj wartość n: "; cin >> n; cout << "Liczby pierwsze od 0 do " << n << ":\n"; bool* tablica = sitoEratostenesa(n); for (int i = 2; i <= n; ++i) { if (tablica[i]) { cout << i << endl; } } delete[] tablica; // Zwolnienie pamięci zaalokowanej dla tablicy return 0; } |
Java – Sito Eratostenesa
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | import java.util.Scanner; public class Main { public static boolean[] sitoEratostenesa(int n) { boolean[] liczbyPierwsze = new boolean[n + 1]; // Tworzenie tablicy for (int i = 0; i <= n; ++i) { liczbyPierwsze[i] = true; // Inicjalizacja wszystkich elementów jako true } liczbyPierwsze[0] = false; // 0 nie jest liczbą pierwszą liczbyPierwsze[1] = false; // 1 nie jest liczbą pierwszą for (int i = 2; i <= n; ++i) { if (liczbyPierwsze[i]) { int j = i + i; while (j <= n) { liczbyPierwsze[j] = false; j += i; } } } return liczbyPierwsze; } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("Podaj wartość n: "); int n = scanner.nextInt(); System.out.println("Liczby pierwsze od 0 do " + n + ":"); boolean[] tablica = sitoEratostenesa(n); for (int i = 2; i <= n; ++i) { if (tablica[i]) { System.out.println(i); } } } } |
Sito Eratostenesa zoptymalizowane – Schemat blokowy i pseudokod
W następującym pseudokodzie oraz schemacie blokowym, możesz znaleźć optymalizacje, dzięki którym Sito Eratostenesa zaczyna działać wydajniej. Ich wytłumaczenie znajdziesz w opisie pseudokodu.
Pseudokod – Zoptymalizowane Sito Eratostenesa
1 2 3 4 5 6 7 8 9 10 11 12 13 | funkcja sitoEratostenesa(n) liczbyPierwsze[1..n] dla i = 1, 2, ... n wykonuj liczbyPierwsze[i] ← prawda liczbyPierwsze[1] ← fałsz dopóki i*i ≤ n wykonuj jeżeli liczbyPierwsze[i] = prawda j ← i * i dopóki j ≤ n wykonuj liczbyPierwsze[j] ← fałsz j ← j + i i←i+1 zwróć liczbyPierwsze |
Opis zoptymalizowanego Sita Eratostenesa – Pseudokod
- 6 linia – Iteracja do pierwiastka kwadratowego z n: Zamiast iterować od 2 do n (jak w wersji podstawowej), gdzie n jest górnym zakresem, iterujemy tylko do pierwiastka kwadratowego z n, czyli i * i ≤ n (bo i ≤ pierwiastek(n) podniesiony do kwadratu, to: i * i ≤ n). Wynika to z faktu, że jeśli n nie jest liczbą pierwszą, to musi mieć przynajmniej jeden dzielnik mniejszy lub równy jego pierwiastkowi kwadratowemu. Dlatego nie ma potrzeby sprawdzania wielokrotności liczb większych niż pierwiastek kwadratowy z n.
Ograniczamy więc ilość iteracji, przykładowo gdy n = 16, jeśli sprawdza się wielokrotności liczb do n, to rozpoczyna się też sprawdzanie wielokrotności liczby 11, której wielokrotność jest przecież większa od n, więc pętla się rozpoczyna i natychmiast zakańcza. Takie działanie jest niepotrzebne, czemu potrafi zapobiec ta optymalizacja. - 8 – 10 linia – Początek od i * i: Wewnętrzna pętla iteruje od i * i zamiast od 2 * i. Dzieje się tak dlatego, że wszystkie mniejsze wielokrotności liczby i już zostały oznaczone jako niebędące liczbami pierwszymi przez poprzednie iteracje. Rozpoczynając od i * i pomijamy już zaznaczone wielokrotności, co znacznie redukuje liczbę operacji. Czyli przykładowo, gdy i = 3, zamiast stosować: 3 + 3 = 6, a następnie 3 + 3 + 3= 9, używa się od razu 3 * 3 = 9, co pozwala zmniejszyć liczbę operacji.
Sito Eratostenesa zoptymalizowane – Python, C++ i Java
Python – Sito Eratostenesa zoptymalizowane
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | import math def sitoEratostenesaZoptymalizowane(n): liczbyPierwsze = [True] * (n + 1) # Tworzenie tablicy z wartościami True liczbyPierwsze[0] = False # 0 nie jest liczbą pierwszą liczbyPierwsze[1] = False # 1 nie jest liczbą pierwszą for i in range(2, int(math.sqrt(n)) + 1): if liczbyPierwsze[i]: for j in range(i * i, n + 1, i): liczbyPierwsze[j] = False return liczbyPierwsze # Wprowadzenie wartości n n = int(input("Podaj wartość n: ")) # Wywołanie funkcji sitoEratostenesaZoptymalizowane i wypisanie wyniku print(f"Liczby pierwsze od 0 do {n}:") tablica = sitoEratostenesaZoptymalizowane(n) for i in range(2, n + 1): if tablica[i]: print(i) |
C++ – Sito Eratostenesa zoptymalizowane
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #include <iostream> #include <cmath> using namespace std; bool* sitoEratostenesaZoptymalizowane(int n) { bool* liczbyPierwsze = new bool[n + 1]; // Tworzenie tablicy dynamicznej z wartościami true for (int i = 0; i <= n; i++) { liczbyPierwsze[i] = true; } liczbyPierwsze[0] = false; // 0 nie jest liczbą pierwszą liczbyPierwsze[1] = false; // 1 nie jest liczbą pierwszą for (int i = 2; i * i <= n; ++i) { if (liczbyPierwsze[i]) { for (int j = i * i; j <= n; j += i) { liczbyPierwsze[j] = false; } } } return liczbyPierwsze; } int main() { // Wprowadzenie wartości n int n; cout << "Podaj wartość n: "; cin >> n; // Wywołanie funkcji sitoEratostenesaZoptymalizowane i wypisanie wyniku cout << "Liczby pierwsze od 0 do " << n << ":\n"; bool* tablica = sitoEratostenesaZoptymalizowane(n); for (int i = 2; i <= n; ++i) { if (tablica[i]) { cout << i << "\n"; } } delete[] tablica; // Zwolnienie zaalokowanej pamięci return 0; } |
Java – Sito Eratostenesa zoptymalizowane
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | import java.util.Scanner; public class Main { public static boolean[] sitoEratostenesaZoptymalizowane(int n) { boolean[] liczbyPierwsze = new boolean[n + 1]; for (int i = 0; i <= n; i++) { liczbyPierwsze[i] = true; } liczbyPierwsze[0] = false; liczbyPierwsze[1] = false; for (int i = 2; i <= Math.sqrt(n); i++) { if (liczbyPierwsze[i]) { for (int j = i * i; j <= n; j += i) { liczbyPierwsze[j] = false; } } } return liczbyPierwsze; } public static void main(String[] args) { // Wprowadzenie wartości n Scanner scanner = new Scanner(System.in); System.out.print("Podaj wartość n: "); int n = scanner.nextInt(); // Wywołanie funkcji sitoEratostenesaZoptymalizowane i wypisanie wyniku System.out.println("Liczby pierwsze od 0 do " + n + ":"); boolean[] tablica = sitoEratostenesaZoptymalizowane(n); for (int i = 2; i <= n; i++) { if (tablica[i]) { System.out.println(i); } } } } |
Podsumowanie – liczby pierwsze w zasięgu Twojej ręki
Sito Eratostenesa to stosunkowo prosty algorytm, który umożliwia wykluczenie określonych wielokrotności jako liczb złożonych (czyli niepierwszych). Okazuje się on niezwykle przydatny w teorii liczb, której podstawy poznałeś w szkole. Zapoznaj się z implementacją tego algorytmu w różnych językach (C++, Python i Java), a także postaraj się zmodyfikować jego działanie, dzięki czemu łatwiej ci będzie zrozumieć zasady postępowania przy korzystaniu z niego.
Najczęściej zadawane pytania o Sito Eratostenesa
Czy algorytm Sita Eratostenesa jest skuteczny dla dużych liczb?
Wydajność Sita Eratostenesa może się pogorszyć wraz ze wzrostem rozmiaru zbioru liczb (pewną pomocą okazuje się optymalizacja). Dlatego dla bardzo dużych wartości efektywniejsze może być zastosowanie innych algorytmów generujących liczby pierwsze.
Jakie są możliwe zastosowania algorytmu Sita Eratostenesa?
Algorytm ten jest pomocny przede wszystkim w przypadku teorii liczb, gdzie w wielu działaniach istnieje potrzeba wygenerowania liczb pierwszych. Może też okazać się przydatny, gdy zachodzi potrzeba określenia, czy dane wartości są pierwsze.
Jakie są inne algorytmy podobne do Sita Eratostenesa?
Wśród najpopularniejszych algorytmów mających na celu sprawdzanie lub generowanie liczb pierwszych można wymienić:
- Test Millera-Rabina,
- Sito Atkina,
- Sito Sundarama.
Jakie są ograniczenia algorytmu Sita Eratostenesa?
Głównym ograniczeniem jest zapotrzebowanie na dużą ilość pamięci, ze względu na tworzenie tablicy równej zakresowi wykorzystywanego do obliczeń zbioru liczb. Poza tym wśród ograniczeń można wymienić niską wydajność przy dużych zakresach, a także brak możliwości dynamicznej aktualizacji wyników (należy powtarzać działanie algorytmu od nowa).
Wpisy, które mogą Cię zainteresować: