diff --git a/src/main/java/com/thealgorithms/maths/SieveOfAtkin.java b/src/main/java/com/thealgorithms/maths/SieveOfAtkin.java new file mode 100644 index 000000000000..ee59d0784ec4 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/SieveOfAtkin.java @@ -0,0 +1,143 @@ +package com.thealgorithms.maths; + +import java.util.ArrayList; +import java.util.List; + +/** + * Implementation of the Sieve of Atkin, an optimized algorithm to generate + * all prime numbers up to a given limit. + * + * The Sieve of Atkin uses quadratic forms and modular arithmetic to identify + * prime candidates, then eliminates multiples of squares. It is more efficient + * than the Sieve of Eratosthenes for large limits. + */ +public final class SieveOfAtkin { + + private SieveOfAtkin() { + // Utlity class; prevent instantiation + } + + /** + * Generates a list of all prime numbers up to the specified limit + * using the Sieve of Atkin algorithm. + * + * @param limit the upper bound up to which primes are generated; must be zero or positive + * @return a list of prime numbers up to the limit; empty if the limit is less than 2 + */ + public static List generatePrimes(int limit) { + if (limit < 1) { + return List.of(); + } + + boolean[] sieve = new boolean[limit + 1]; + int sqrtLimit = (int) Math.sqrt(limit); + + markQuadraticResidues(limit, sqrtLimit, sieve); + eliminateMultiplesOfSquares(limit, sqrtLimit, sieve); + + List primes = new ArrayList<>(); + if (limit >= 2) { + primes.add(2); + } + if (limit >= 3) { + primes.add(3); + } + + for (int i = 5; i <= limit; i++) { + if (sieve[i]) { + primes.add(i); + } + } + + return primes; + } + + /** + * Marks numbers in the sieve as prime candidates based on quadratic residues. + * + * This method iterates over all x and y up to sqrt(limit) and applies + * the three quadratic forms used in the Sieve of Atkin. Numbers satisfying + * the modulo conditions are toggled in the sieve array. + * + * @param limit the upper bound for primes + * @param sqrtLimit square root of the limit + * @param sieve boolean array representing potential primes + */ + private static void markQuadraticResidues(int limit, int sqrtLimit, boolean[] sieve) { + for (int x = 1; x <= sqrtLimit; x++) { + for (int y = 1; y <= sqrtLimit; y++) { + applyQuadraticForm(4 * x * x + y * y, limit, sieve, 1, 5); + applyQuadraticForm(3 * x * x + y * y, limit, sieve, 7); + applyQuadraticForm(3 * x * x - y * y, limit, sieve, 11, x > y); + } + } + } + + /** + * Toggles the sieve entry for a number if it satisfies one modulo condition. + * + * @param n the number to check + * @param limit upper bound of primes + * @param sieve boolean array representing potential primes + * @param modulo the modulo condition number to check + */ + private static void applyQuadraticForm(int n, int limit, boolean[] sieve, int modulo) { + if (n <= limit && n % 12 == modulo) { + sieve[n] ^= true; + } + } + + /** + * Toggles the sieve entry for a number if it satisfies either of two modulo conditions. + * + * @param n the number to check + * @param limit upper bound of primes + * @param sieve boolean array representing potential primes + * @param modulo1 first modulo condition number to check + * @param modulo2 second modulo condition number to check + */ + private static void applyQuadraticForm(int n, int limit, boolean[] sieve, int modulo1, int modulo2) { + if (n <= limit && (n % 12 == modulo1 || n % 12 == modulo2)) { + sieve[n] ^= true; + } + } + + /** + * Toggles the sieve entry for a number if it satisfies the modulo condition and an additional boolean condition. + * + * This version is used for the quadratic form 3*x*x - y*y, which requires x > y. + * + * @param n the number to check + * @param limit upper bound of primes + * @param sieve boolean array representing potential primes + * @param modulo the modulo condition number to check + * @param condition an additional boolean condition that must be true + */ + private static void applyQuadraticForm(int n, int limit, boolean[] sieve, int modulo, boolean condition) { + if (condition && n <= limit && n % 12 == modulo) { + sieve[n] ^= true; + } + } + + /** + * Eliminates numbers that are multiples of squares from the sieve. + * + * All numbers that are multiples of i*i (where i is marked as prime) are + * marked non-prime to finalize the sieve. This ensures only actual primes remain. + * + * @param limit the upper bound for primes + * @param sqrtLimit square root of the limit + * @param sieve boolean array representing potential primes + */ + private static void eliminateMultiplesOfSquares(int limit, int sqrtLimit, boolean[] sieve) { + for (int i = 5; i <= sqrtLimit; i++) { + if (!sieve[i]) { + continue; + } + int square = i * i; + for (int j = square; j <= limit; j += square) { + sieve[j] = false; + } + } + } +} diff --git a/src/test/java/com/thealgorithms/maths/SieveOfAtkinTest.java b/src/test/java/com/thealgorithms/maths/SieveOfAtkinTest.java new file mode 100644 index 000000000000..8eca45c9abb8 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/SieveOfAtkinTest.java @@ -0,0 +1,47 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the {@code SieveOfAtkin} class. + */ +class SieveOfAtkinTest { + + @Test + void testGeneratePrimesLimit10() { + List primes = SieveOfAtkin.generatePrimes(10); + // Assert the full expected list of primes + List expected = List.of(2, 3, 5, 7); + assertEquals(expected, primes, "Primes up to 10 should match expected list"); + } + + @Test + void testGeneratePrimesLimit2() { + List primes = SieveOfAtkin.generatePrimes(2); + List expected = List.of(2); + assertEquals(expected, primes, "Primes up to 2 should include 2"); + } + + @Test + void testGeneratePrimesLimit1() { + List primes = SieveOfAtkin.generatePrimes(1); + assertTrue(primes.isEmpty(), "Primes list should be empty when limit < 2"); + } + + @Test + void testGeneratePrimesLimit50() { + List primes = SieveOfAtkin.generatePrimes(50); + List expected = List.of(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47); + assertEquals(expected, primes, "Primes up to 50 should match expected list"); + } + + @Test + void testGeneratePrimesNegativeLimit() { + List primes = SieveOfAtkin.generatePrimes(-10); + assertTrue(primes.isEmpty(), "Primes list should be empty for negative limit"); + } +}