|
1 | 1 | package com.fishercoder.solutions; |
2 | 2 |
|
3 | | -/** |
4 | | - * 204. Count Primes |
5 | | - * |
6 | | - * Description: |
7 | | -
|
8 | | - Count the number of prime numbers less than a non-negative number, n. |
9 | | -
|
10 | | - Hint: |
11 | | -
|
12 | | - Let's start with a isPrime function. To determine if a number is prime, we need to check if it is not divisible by any number less than n. |
13 | | - The runtime complexity of isPrime function would be O(n) and hence counting the total prime numbers up to n would be O(n2). Could we do better? |
14 | | -
|
15 | | - As we know the number must not be divisible by any number > n / 2, |
16 | | - we can immediately cut the total iterations half by dividing only up to n / 2. Could we still do better? |
17 | | -
|
18 | | - Let's write down all of 12's factors: |
19 | | -
|
20 | | - 2 × 6 = 12 |
21 | | - 3 × 4 = 12 |
22 | | - 4 × 3 = 12 |
23 | | - 6 × 2 = 12 |
24 | | - As you can see, calculations of 4 × 3 and 6 × 2 are not necessary. Therefore, we only need to consider factors up to √n because, |
25 | | - if n is divisible by some number p, then n = p × q and since p ≤ q, we could derive that p ≤ √n. |
26 | | -
|
27 | | - Our total runtime has now improved to O(n1.5), which is slightly better. Is there a faster approach? |
28 | | -
|
29 | | - public int countPrimes(int n) { |
30 | | - int count = 0; |
31 | | - for (int i = 1; i < n; i++) { |
32 | | - if (isPrime(i)) count++; |
33 | | - } |
34 | | - return count; |
35 | | - } |
36 | | -
|
37 | | - private boolean isPrime(int num) { |
38 | | - if (num <= 1) return false; |
39 | | - // Loop's ending condition is i * i <= num instead of i <= sqrt(num) |
40 | | - // to avoid repeatedly calling an expensive function sqrt(). |
41 | | - for (int i = 2; i * i <= num; i++) { |
42 | | - if (num % i == 0) return false; |
43 | | - } |
44 | | - return true; |
45 | | - } |
46 | | -
|
47 | | - The Sieve of Eratosthenes is one of the most efficient ways to find all prime numbers up to n. |
48 | | - But don't let that name scare you, I promise that the concept is surprisingly simple. |
49 | | -
|
50 | | - Sieve of Eratosthenes: algorithm steps for primes below 121. "Sieve of Eratosthenes Animation" by SKopp is licensed under CC BY 2.0. |
51 | | - We start off with a table of n numbers. Let's look at the first number, 2. We know all multiples of 2 must not be primes, so we mark |
52 | | - them off as non-primes. Then we look at the next number, 3. Similarly, all multiples of 3 such as 3 × 2 = 6, 3 × 3 = 9, ... must not |
53 | | - be primes, so we mark them off as well. Now we look at the next number, 4, which was already marked off. |
54 | | - What does this tell you? Should you mark off all multiples of 4 as well? |
55 | | - 4 is not a prime because it is divisible by 2, which means all multiples of 4 must also be divisible by 2 and were already marked off. |
56 | | - So we can skip 4 immediately and go to the next number, 5. |
57 | | - Now, all multiples of 5 such as 5 × 2 = 10, 5 × 3 = 15, 5 × 4 = 20, 5 × 5 = 25, ... can be marked off. |
58 | | - There is a slight optimization here, we do not need to start from 5 × 2 = 10. Where should we start marking off? |
59 | | -
|
60 | | - In fact, we can mark off multiples of 5 starting at 5 × 5 = 25, because 5 × 2 = 10 was already marked off by multiple of 2, |
61 | | - similarly 5 × 3 = 15 was already marked off by multiple of 3. |
62 | | - Therefore, if the current number is p, we can always mark off multiples of p starting at p2, then in increments of p: p2 + p, p2 + 2p, ... |
63 | | - Now what should be the terminating loop condition? |
64 | | -
|
65 | | - It is easy to say that the terminating loop condition is p < n, which is certainly correct but not efficient. Do you still remember Hint #3? |
66 | | - Yes, the terminating loop condition can be p < √n, as all non-primes ≥ √n must have already been marked off. When the loop terminates, |
67 | | - all the numbers in the table that are non-marked are prime. |
68 | | -
|
69 | | - The Sieve of Eratosthenes uses an extra O(n) memory and its runtime complexity is O(n log log n). |
70 | | - For the more mathematically inclined readers, you can read more about its algorithm complexity on Wikipedia. |
71 | | -
|
72 | | - public int countPrimes(int n) { |
73 | | - boolean[] isPrime = new boolean[n]; |
74 | | - for (int i = 2; i < n; i++) { |
75 | | - isPrime[i] = true; |
76 | | - } |
77 | | -
|
78 | | - // Loop's ending condition is i * i < n instead of i < sqrt(n) |
79 | | - // to avoid repeatedly calling an expensive function sqrt(). |
80 | | -
|
81 | | - for (int i = 2; i * i < n; i++) { |
82 | | - if (!isPrime[i]) continue; |
83 | | - for (int j = i * i; j < n; j += i) { |
84 | | - isPrime[j] = false; |
85 | | - } |
86 | | - } |
87 | | - int count = 0; |
88 | | - for (int i = 2; i < n; i++) { |
89 | | - if (isPrime[i]) count++; |
90 | | - } |
91 | | -
|
92 | | - return count; |
93 | | - } |
94 | | - */ |
95 | 3 | public class _204 { |
96 | 4 |
|
97 | 5 | public static class Solution1 { |
|
0 commit comments