1. 程式人生 > 實用技巧 >【LeetCode-204】--計算[2,n]的所有質數--

【LeetCode-204】--計算[2,n]的所有質數--

轉載自:https://leetcode-cn.com/problems/count-primes/solution/ji-shu-zhi-shu-by-leetcode-solution/

統計所有小於非負整數n的質數的數量。

示例 1:

輸入:n = 10
輸出:4
解釋:小於 10 的質數一共有 4 個, 它們是 2, 3, 5, 7 。
示例 2:

輸入:n = 0
輸出:0
示例 3:

輸入:n = 1
輸出:0

來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/count-primes
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。

程式碼路徑:

方法1:

  最簡單粗暴的方法,雙層遍歷,沒什麼好講的

  時間複雜度:O(n2)

 1     public static int countPrimes2(int n) {
 2         int count = 0;
 3         for (int i = 2; i < n; i++) {
 4             if (isPrimes(i)) {
 5                 count++;
 6             }
 7         }
 8         return count;
 9     }
10 
11     public static
boolean isPrimes(int i) { 12 for (int j = 2; j < i; j++) { 13 if (i % j == 0) { 14 return false; 15 } 16 } 17 return true; 18 }

方法2:

  基於方法1的優化,在方法1的isPrimes中存在很多次的冗餘的計算,在isPrimes方法中j不需要遍歷到i,而只需要遍歷到sqrt(n);為什麼呢,假設n=12;

12 = 2 × 6
12 = 3 × 4
12 = sqrt(12) × sqrt(12)
12 = 4 × 3 12 = 6 × 2

可以看到,後兩個乘積就是前面兩個反過來,反轉臨界點就在 sqrt(n)。

換句話說,如果在 [2,sqrt(n)] 這個區間之內沒有發現可整除因子,就可以直接斷定 n 是素數了,因為在區間 [sqrt(n),n] 也一定不會發現可整除因子。

時間複雜度:O(n√n)

 1     public static int countPrimes2(int n) {
 2         int count = 0;
 3         for (int i = 2; i < n; i++) {
 4             if (isPrimes(i)) {
 5                 count++;
 6             }
 7         }
 8         return count;
 9     }
10 
11     public static boolean isPrimes(int i) {
12         //j<i 優化 j*j<i
13         for (int j = 2; j * j < i; j++) {
14             if (i % j == 0) {
15                 return false;
16             }
17         }
18         return true;
19     }

方法3:

  基於方法2,我們可以思考,一開始2為質數,那麼2*i(2*i<n)的所有數是不是都不是質數了,因為能被2整除,i為3,是質數,那麼3*i的所有數也都不是質數了,那麼4剛已經被2標記不是質數了,依此類推...,該演算法由希臘數學家厄拉多塞(\rm EratosthenesEratosthenes)提出,稱為厄拉多塞篩法,簡稱埃氏篩。

時間複雜度:O(nloglog n)

 1     public static int countPrimes(int n) {
 2         //所有數值的狀態為1,假設所有數都為質數
 3         int[] isPrime = new int[n];
 4         Arrays.fill(isPrime, 1);
 5         int ans = 0;
 6         for (int i = 2; i < n; ++i) {
 7             //當isPrime[i]為1時說明,小於i的數都不能將i除盡,所以計數+1
 8             if (isPrime[i] == 1) {
 9                 ans += 1;
10                 if ((long) i * i < n) {
11                     //這裡從i*i開始,因為 2*i,3*i,… 這些數一定在 i 之前就被其他數的倍數標記過了,和方法2的思想是一樣的,j+=i,倍數增長嘛
12                     for (int j = i * i; j < n; j += i) {
13                         isPrime[j] = 0;
14                     }
15                 }
16             }
17         }
18         return ans;
19     }

方法4:

  

 1     public static int countPrimes1(int n) {
 2         boolean[] isPrimes = new boolean[n];
 3         List<Integer> primes = new ArrayList<>(n);
 4         Arrays.fill(isPrimes, true);
 5 
 6         for (int i = 2; i < n; i++) {
 7             if (isPrimes[i]) {
 8                 primes.add(i);
 9             }
10             for (int j = 0; j < primes.size() && i * primes.get(j) < n; j++) {
11                 isPrimes[i * primes.get(j)] = false;
12                 if (i % primes.get(j) == 0) {
13                     break;
14                 }
15             }
16         }
17 
18         return primes.size();
19     }