Skip to content

Commit 061463a

Browse files
saahilmahatoalxkm
andauthored
feat: add sieve of atkin algorithm (#6709)
* feat: add sieve of atkin algorithm * fix: add full test coverage * refactor: if condition braces --------- Co-authored-by: a <[email protected]>
1 parent f9edb70 commit 061463a

File tree

2 files changed

+190
-0
lines changed

2 files changed

+190
-0
lines changed
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package com.thealgorithms.maths;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
/**
7+
* Implementation of the Sieve of Atkin, an optimized algorithm to generate
8+
* all prime numbers up to a given limit.
9+
*
10+
* The Sieve of Atkin uses quadratic forms and modular arithmetic to identify
11+
* prime candidates, then eliminates multiples of squares. It is more efficient
12+
* than the Sieve of Eratosthenes for large limits.
13+
*/
14+
public final class SieveOfAtkin {
15+
16+
private SieveOfAtkin() {
17+
// Utlity class; prevent instantiation
18+
}
19+
20+
/**
21+
* Generates a list of all prime numbers up to the specified limit
22+
* using the Sieve of Atkin algorithm.
23+
*
24+
* @param limit the upper bound up to which primes are generated; must be zero or positive
25+
* @return a list of prime numbers up to the limit; empty if the limit is less than 2
26+
*/
27+
public static List<Integer> generatePrimes(int limit) {
28+
if (limit < 1) {
29+
return List.of();
30+
}
31+
32+
boolean[] sieve = new boolean[limit + 1];
33+
int sqrtLimit = (int) Math.sqrt(limit);
34+
35+
markQuadraticResidues(limit, sqrtLimit, sieve);
36+
eliminateMultiplesOfSquares(limit, sqrtLimit, sieve);
37+
38+
List<Integer> primes = new ArrayList<>();
39+
if (limit >= 2) {
40+
primes.add(2);
41+
}
42+
if (limit >= 3) {
43+
primes.add(3);
44+
}
45+
46+
for (int i = 5; i <= limit; i++) {
47+
if (sieve[i]) {
48+
primes.add(i);
49+
}
50+
}
51+
52+
return primes;
53+
}
54+
55+
/**
56+
* Marks numbers in the sieve as prime candidates based on quadratic residues.
57+
*
58+
* This method iterates over all x and y up to sqrt(limit) and applies
59+
* the three quadratic forms used in the Sieve of Atkin. Numbers satisfying
60+
* the modulo conditions are toggled in the sieve array.
61+
*
62+
* @param limit the upper bound for primes
63+
* @param sqrtLimit square root of the limit
64+
* @param sieve boolean array representing potential primes
65+
*/
66+
private static void markQuadraticResidues(int limit, int sqrtLimit, boolean[] sieve) {
67+
for (int x = 1; x <= sqrtLimit; x++) {
68+
for (int y = 1; y <= sqrtLimit; y++) {
69+
applyQuadraticForm(4 * x * x + y * y, limit, sieve, 1, 5);
70+
applyQuadraticForm(3 * x * x + y * y, limit, sieve, 7);
71+
applyQuadraticForm(3 * x * x - y * y, limit, sieve, 11, x > y);
72+
}
73+
}
74+
}
75+
76+
/**
77+
* Toggles the sieve entry for a number if it satisfies one modulo condition.
78+
*
79+
* @param n the number to check
80+
* @param limit upper bound of primes
81+
* @param sieve boolean array representing potential primes
82+
* @param modulo the modulo condition number to check
83+
*/
84+
private static void applyQuadraticForm(int n, int limit, boolean[] sieve, int modulo) {
85+
if (n <= limit && n % 12 == modulo) {
86+
sieve[n] ^= true;
87+
}
88+
}
89+
90+
/**
91+
* Toggles the sieve entry for a number if it satisfies either of two modulo conditions.
92+
*
93+
* @param n the number to check
94+
* @param limit upper bound of primes
95+
* @param sieve boolean array representing potential primes
96+
* @param modulo1 first modulo condition number to check
97+
* @param modulo2 second modulo condition number to check
98+
*/
99+
private static void applyQuadraticForm(int n, int limit, boolean[] sieve, int modulo1, int modulo2) {
100+
if (n <= limit && (n % 12 == modulo1 || n % 12 == modulo2)) {
101+
sieve[n] ^= true;
102+
}
103+
}
104+
105+
/**
106+
* Toggles the sieve entry for a number if it satisfies the modulo condition and an additional boolean condition.
107+
*
108+
* This version is used for the quadratic form 3*x*x - y*y, which requires x > y.
109+
*
110+
* @param n the number to check
111+
* @param limit upper bound of primes
112+
* @param sieve boolean array representing potential primes
113+
* @param modulo the modulo condition number to check
114+
* @param condition an additional boolean condition that must be true
115+
*/
116+
private static void applyQuadraticForm(int n, int limit, boolean[] sieve, int modulo, boolean condition) {
117+
if (condition && n <= limit && n % 12 == modulo) {
118+
sieve[n] ^= true;
119+
}
120+
}
121+
122+
/**
123+
* Eliminates numbers that are multiples of squares from the sieve.
124+
*
125+
* All numbers that are multiples of i*i (where i is marked as prime) are
126+
* marked non-prime to finalize the sieve. This ensures only actual primes remain.
127+
*
128+
* @param limit the upper bound for primes
129+
* @param sqrtLimit square root of the limit
130+
* @param sieve boolean array representing potential primes
131+
*/
132+
private static void eliminateMultiplesOfSquares(int limit, int sqrtLimit, boolean[] sieve) {
133+
for (int i = 5; i <= sqrtLimit; i++) {
134+
if (!sieve[i]) {
135+
continue;
136+
}
137+
int square = i * i;
138+
for (int j = square; j <= limit; j += square) {
139+
sieve[j] = false;
140+
}
141+
}
142+
}
143+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.thealgorithms.maths;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
5+
6+
import java.util.List;
7+
import org.junit.jupiter.api.Test;
8+
9+
/**
10+
* Unit tests for the {@code SieveOfAtkin} class.
11+
*/
12+
class SieveOfAtkinTest {
13+
14+
@Test
15+
void testGeneratePrimesLimit10() {
16+
List<Integer> primes = SieveOfAtkin.generatePrimes(10);
17+
// Assert the full expected list of primes
18+
List<Integer> expected = List.of(2, 3, 5, 7);
19+
assertEquals(expected, primes, "Primes up to 10 should match expected list");
20+
}
21+
22+
@Test
23+
void testGeneratePrimesLimit2() {
24+
List<Integer> primes = SieveOfAtkin.generatePrimes(2);
25+
List<Integer> expected = List.of(2);
26+
assertEquals(expected, primes, "Primes up to 2 should include 2");
27+
}
28+
29+
@Test
30+
void testGeneratePrimesLimit1() {
31+
List<Integer> primes = SieveOfAtkin.generatePrimes(1);
32+
assertTrue(primes.isEmpty(), "Primes list should be empty when limit < 2");
33+
}
34+
35+
@Test
36+
void testGeneratePrimesLimit50() {
37+
List<Integer> primes = SieveOfAtkin.generatePrimes(50);
38+
List<Integer> expected = List.of(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47);
39+
assertEquals(expected, primes, "Primes up to 50 should match expected list");
40+
}
41+
42+
@Test
43+
void testGeneratePrimesNegativeLimit() {
44+
List<Integer> primes = SieveOfAtkin.generatePrimes(-10);
45+
assertTrue(primes.isEmpty(), "Primes list should be empty for negative limit");
46+
}
47+
}

0 commit comments

Comments
 (0)