Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 143 additions & 0 deletions src/main/java/com/thealgorithms/maths/SieveOfAtkin.java
Original file line number Diff line number Diff line change
@@ -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<Integer> 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<Integer> 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;
}
}
}
}
47 changes: 47 additions & 0 deletions src/test/java/com/thealgorithms/maths/SieveOfAtkinTest.java
Original file line number Diff line number Diff line change
@@ -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<Integer> primes = SieveOfAtkin.generatePrimes(10);
// Assert the full expected list of primes
List<Integer> expected = List.of(2, 3, 5, 7);
assertEquals(expected, primes, "Primes up to 10 should match expected list");
}

@Test
void testGeneratePrimesLimit2() {
List<Integer> primes = SieveOfAtkin.generatePrimes(2);
List<Integer> expected = List.of(2);
assertEquals(expected, primes, "Primes up to 2 should include 2");
}

@Test
void testGeneratePrimesLimit1() {
List<Integer> primes = SieveOfAtkin.generatePrimes(1);
assertTrue(primes.isEmpty(), "Primes list should be empty when limit < 2");
}

@Test
void testGeneratePrimesLimit50() {
List<Integer> primes = SieveOfAtkin.generatePrimes(50);
List<Integer> 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<Integer> primes = SieveOfAtkin.generatePrimes(-10);
assertTrue(primes.isEmpty(), "Primes list should be empty for negative limit");
}
}