|
1 | 1 | /** |
2 | | - * Generate a compressed prime sieve using bit manipulation. The idea is that each bit represents a |
3 | | - * boolean value indicating whether a number is prime or not. This saves a lot of room when creating |
4 | | - * the sieve. In this implementation I store all odd numbers in individual longs meaning that for |
5 | | - * each long I use I can represent a range of 128 numbers (even numbers are omitted because they are |
6 | | - * not prime, with the exception of 2 which is handled as a special case). |
| 2 | + * Generates a compressed prime sieve using bit manipulation. Each bit represents whether an odd |
| 3 | + * number is prime or not. Even numbers are omitted (except 2, handled as a special case), so each |
| 4 | + * long covers a range of 128 numbers. |
7 | 5 | * |
8 | | - * <p>Time Complexity: ~O(nloglogn) |
| 6 | + * <p>Time: ~O(n log(log(n))) |
9 | 7 | * |
10 | | - * <p>Compile: javac -d src/main/java |
11 | | - * src/main/java/com/williamfiset/algorithms/math/CompressedPrimeSieve.java |
12 | | - * |
13 | | - * <p>Run: java -cp src/main/java com/williamfiset/algorithms/math/CompressedPrimeSieve |
| 8 | + * <p>Space: O(n / 128) longs |
14 | 9 | * |
15 | 10 | * @author William Fiset, william.alexandre.fiset@gmail.com |
16 | 11 | */ |
17 | 12 | package com.williamfiset.algorithms.math; |
18 | 13 |
|
19 | 14 | public class CompressedPrimeSieve { |
| 15 | + |
20 | 16 | private static final double NUM_BITS = 128.0; |
21 | 17 | private static final int NUM_BITS_SHIFT = 7; // 2^7 = 128 |
22 | 18 |
|
23 | | - // Sets the bit representing n to 1 indicating this number is not prime |
| 19 | + // Marks n as not prime by setting its bit to 1. |
24 | 20 | private static void setBit(long[] arr, int n) { |
25 | | - if ((n & 1) == 0) return; // n is even |
| 21 | + if ((n & 1) == 0) |
| 22 | + return; |
26 | 23 | arr[n >> NUM_BITS_SHIFT] |= 1L << ((n - 1) >> 1); |
27 | 24 | } |
28 | 25 |
|
29 | | - // Returns true if the bit for n is off (meaning n is a prime). |
30 | | - // Note: do use this method to access numbers outside your prime sieve range! |
| 26 | + // Returns true if n's bit is unset (meaning n is prime). |
31 | 27 | private static boolean isNotSet(long[] arr, int n) { |
32 | | - if (n < 2) return false; // n is not prime |
33 | | - if (n == 2) return true; // two is prime |
34 | | - if ((n & 1) == 0) return false; // n is even |
| 28 | + if (n < 2) |
| 29 | + return false; |
| 30 | + if (n == 2) |
| 31 | + return true; |
| 32 | + if ((n & 1) == 0) |
| 33 | + return false; |
35 | 34 | long chunk = arr[n >> NUM_BITS_SHIFT]; |
36 | 35 | long mask = 1L << ((n - 1) >> 1); |
37 | 36 | return (chunk & mask) != mask; |
38 | 37 | } |
39 | 38 |
|
40 | | - // Returns true/false depending on whether n is prime. |
| 39 | + /** Returns true if n is prime according to the given sieve. */ |
41 | 40 | public static boolean isPrime(long[] sieve, int n) { |
42 | 41 | return isNotSet(sieve, n); |
43 | 42 | } |
44 | 43 |
|
45 | | - // Returns an array of longs with each bit indicating whether a number |
46 | | - // is prime or not. Use the isNotSet and setBit methods to toggle to bits for each number. |
| 44 | + /** |
| 45 | + * Builds a compressed prime sieve for all numbers up to {@code limit}. |
| 46 | + * |
| 47 | + * @param limit the upper bound (inclusive) for the sieve. |
| 48 | + * @return a bit-packed array where each bit indicates whether an odd number is composite. |
| 49 | + */ |
47 | 50 | public static long[] primeSieve(int limit) { |
48 | | - final int numChunks = (int) Math.ceil(limit / NUM_BITS); |
49 | | - final int sqrtLimit = (int) Math.sqrt(limit); |
50 | | - // if (limit < 2) return 0; // uncomment for primeCount purposes |
51 | | - // int primeCount = (int) Math.ceil(limit / 2.0); // Counts number of primes <= limit |
| 51 | + int numChunks = (int) Math.ceil(limit / NUM_BITS); |
| 52 | + int sqrtLimit = (int) Math.sqrt(limit); |
52 | 53 | long[] chunks = new long[numChunks]; |
53 | | - chunks[0] = 1; // 1 as not prime |
| 54 | + chunks[0] = 1; // Mark 1 as not prime. |
54 | 55 | for (int i = 3; i <= sqrtLimit; i += 2) |
55 | 56 | if (isNotSet(chunks, i)) |
56 | 57 | for (int j = i * i; j <= limit; j += i) |
57 | | - if (isNotSet(chunks, j)) { |
| 58 | + if (isNotSet(chunks, j)) |
58 | 59 | setBit(chunks, j); |
59 | | - // primeCount--; |
60 | | - } |
61 | 60 | return chunks; |
62 | 61 | } |
63 | 62 |
|
64 | | - /* Example usage. */ |
65 | | - |
66 | 63 | public static void main(String[] args) { |
67 | | - final int limit = 200; |
68 | | - long[] sieve = CompressedPrimeSieve.primeSieve(limit); |
69 | | - |
70 | | - for (int i = 0; i <= limit; i++) { |
71 | | - if (CompressedPrimeSieve.isPrime(sieve, i)) { |
| 64 | + int limit = 200; |
| 65 | + long[] sieve = primeSieve(limit); |
| 66 | + for (int i = 0; i <= limit; i++) |
| 67 | + if (isPrime(sieve, i)) |
72 | 68 | System.out.printf("%d is prime!\n", i); |
73 | | - } |
74 | | - } |
75 | 69 | } |
76 | 70 | } |
0 commit comments