|
| 1 | +# How to find prime Numbers efficiently |
| 2 | + |
| 3 | +**Translator: [shazi4399](https://github.com/shazi4399)** |
| 4 | + |
| 5 | +**Author: [labuladong](https://github.com/labuladong)** |
| 6 | + |
| 7 | +The definition of a prime number seems simple,which is said to be prime number if it can be divided by 1 and itself. |
| 8 | + |
| 9 | +However,don't think that the definition of prime numbers is simple. I am afraid that few people can write a prime-related algorithm that works really efficiently. Let's say you write a function like this: |
| 10 | + |
| 11 | +```java |
| 12 | +// Returns several primes in the interval [2, n) |
| 13 | +int countPrimes(int n) |
| 14 | + |
| 15 | +// E.g. countPrimes (10) returns 4 |
| 16 | +// Because 2,3,5,7 is prime numbers |
| 17 | +``` |
| 18 | + |
| 19 | +How would you progrma this function? I think you maybe write like this: |
| 20 | + |
| 21 | +```java |
| 22 | +int countPrimes(int n) { |
| 23 | + int count = 0; |
| 24 | + for (int i = 2; i < n; i++) |
| 25 | + if (isPrim(i)) count++; |
| 26 | + return count; |
| 27 | +} |
| 28 | + |
| 29 | +// Determines whether integer n is prime |
| 30 | +boolean isPrime(int n) { |
| 31 | + for (int i = 2; i < n; i++) |
| 32 | + if (n % i == 0) |
| 33 | + // There are other divisibility factors |
| 34 | + return false; |
| 35 | + return true; |
| 36 | +} |
| 37 | +``` |
| 38 | + |
| 39 | +The time complexity is O (n ^ 2), which is a big problem.**First of all, the idea of using the isPrime function to assist is not efficient; and even if you want to use the isPrime function, there is computational redundancy in writing the algorithm**. |
| 40 | + |
| 41 | +Let's briefly talk about **how to write an algorithm if you want to determine whether a number is prime or not**. Just slightly modify the for loop condition in the isPrim code above: |
| 42 | + |
| 43 | +```java |
| 44 | +boolean isPrime(int n) { |
| 45 | + for (int i = 2; i * i <= n; i++) |
| 46 | + ... |
| 47 | +} |
| 48 | +``` |
| 49 | + |
| 50 | +In other words, `i` does not need to traverse to` n`, but only to `sqrt (n)`. Why? let's take an example, suppose `n = 12`. |
| 51 | + |
| 52 | +```java |
| 53 | +12 = 2 × 6 |
| 54 | +12 = 3 × 4 |
| 55 | +12 = sqrt(12) × sqrt(12) |
| 56 | +12 = 4 × 3 |
| 57 | +12 = 6 × 2 |
| 58 | +``` |
| 59 | + |
| 60 | +As you can see, the last two products are the reverse of the previous two, and the critical point of inversion is at `sqrt (n)`. |
| 61 | + |
| 62 | +In other words, if no divisible factor is found within the interval `[[2, sqrt (n)]`, you can directly conclude that `n` is a prime number, because in the interval `[[sqrt (n), n] ` Nor will you find a divisible factor. |
| 63 | + |
| 64 | +Now, the time complexity of the `isPrime` function is reduced to O (sqrt (N)), ** but we don't actually need this function to implement the` countPrimes` function. The above just hope that readers understand the meaning of `sqrt (n)`, because it will be used again later. |
| 65 | + |
| 66 | + |
| 67 | +### Efficient implementation `countPrimes` |
| 68 | + |
| 69 | +The core idea of efficiently solving this problem is to reverse the conventional idea above: |
| 70 | + |
| 71 | +First from 2, we know that 2 is a prime number, then 2 × 2 = 4, 3 × 2 = 6, 4 × 2 = 8 ... all are not prime numbers. |
| 72 | + |
| 73 | +Then we found that 3 is also a prime number, so 3 × 2 = 6, 3 × 3 = 9, 3 × 4 = 12 ... are also impossible to be prime numbers. |
| 74 | + |
| 75 | +Seeing this, do you understand the logic of this exclusion method a bit? First look at our first version of the code: |
| 76 | + |
| 77 | +```java |
| 78 | +int countPrimes(int n) { |
| 79 | + boolean[] isPrim = new boolean[n]; |
| 80 | + // Initialize the arrays to true |
| 81 | + Arrays.fill(isPrim, true); |
| 82 | + |
| 83 | + for (int i = 2; i < n; i++) |
| 84 | + if (isPrim[i]) |
| 85 | + // Multiples of i cannot be prime |
| 86 | + for (int j = 2 * i; j < n; j += i) |
| 87 | + isPrim[j] = false; |
| 88 | + |
| 89 | + int count = 0; |
| 90 | + for (int i = 2; i < n; i++) |
| 91 | + if (isPrim[i]) count++; |
| 92 | + |
| 93 | + return count; |
| 94 | +} |
| 95 | +``` |
| 96 | + |
| 97 | +If you can understand the above code, then you have mastered the overall idea, but there are two subtle areas that can be optimized. |
| 98 | + |
| 99 | +First of all, recall the `isPrime` function that just judges whether a number is prime. Due to the symmetry of the factors, the for loop only needs to traverse` [2, sqrt (n)] `. Here is similar, our outer for loop only needs to traverse to `sqrt (n)`: |
| 100 | + |
| 101 | +```java |
| 102 | +for (int i = 2; i * i < n; i++) |
| 103 | + if (isPrim[i]) |
| 104 | + ... |
| 105 | +``` |
| 106 | + |
| 107 | +In addition, it is difficult to notice that the inner for loop can also be optimized. Our previous approach was: |
| 108 | + |
| 109 | +```java |
| 110 | +for (int j = 2 * i; j < n; j += i) |
| 111 | + isPrim[j] = false; |
| 112 | +``` |
| 113 | + |
| 114 | +This can mark all integer multiples of `i` as` false`, but there is still computational redundancy. |
| 115 | + |
| 116 | +For example, when `n = 25` and` i = 4`, the algorithm will mark numbers such as 4 × 2 = 8, 4 × 3 = 12, and so on, but these two numbers have been marked by 2 × 4 and 3 × 4 that is `i = 2` and` i = 3`. |
| 117 | + |
| 118 | +We can optimize it slightly so that `j` traverses from the square of` i` instead of starting from `2 * i`: |
| 119 | + |
| 120 | +```java |
| 121 | +for (int j = i * i; j < n; j += i) |
| 122 | + isPrim[j] = false; |
| 123 | +``` |
| 124 | + |
| 125 | +In this way, the algorithm for counting prime numbers is efficiently implemented. In fact, this algorithm has a name, which called Sieve of Eratosthenes. Take a look at the complete final code: |
| 126 | + |
| 127 | +```java |
| 128 | +int countPrimes(int n) { |
| 129 | + boolean[] isPrim = new boolean[n]; |
| 130 | + Arrays.fill(isPrim, true); |
| 131 | + for (int i = 2; i * i < n; i++) |
| 132 | + if (isPrim[i]) |
| 133 | + for (int j = i * i; j < n; j += i) |
| 134 | + isPrim[j] = false; |
| 135 | + |
| 136 | + int count = 0; |
| 137 | + for (int i = 2; i < n; i++) |
| 138 | + if (isPrim[i]) count++; |
| 139 | + |
| 140 | + return count; |
| 141 | +} |
| 142 | +``` |
| 143 | + |
| 144 | +**The time complexity of this algorithm is difficult to calculate**.It is obvious that the time is related to these two nested for loops. The operands should be: |
| 145 | + |
| 146 | + n/2 + n/3 + n/5 + n/7 + ... |
| 147 | += n × (1/2 + 1/3 + 1/5 + 1/7...) |
| 148 | + |
| 149 | +In parentheses, ther is the inverse of the prime number .The final result is O(N * loglogN),and readers interested in this can refer to the time complexity of the algorithm |
| 150 | + |
| 151 | +That is all about how to find prime Numbers.The seemingly simple problem does has a lot of details to polish |
0 commit comments