統計所有小於非負整數 n 的質數的數量。
示例:
輸入: 10
輸出: 4
解釋: 小於 10 的質數一共有 4 個, 它們是 2, 3, 5, 7 。
這個題目思路很簡單,但是可能效率不高,裡面有一些小trick需要注意,所以在這裡記錄一下優化過程。
思路1: 逐個判斷每個數是否質數,(超時)
class solution:
def countprimes(self, n: int) -> int:
def isprime(val):
if val <= 1:
return false
for i in range(2, int(math.sqrt(val)) + 1):
if val % i == 0:
return false
return true
count = 0
for i in range(n):
if isprime(i):
count += 1
return count
思路2:厄拉多塞篩法
實現上的小細節: 可以想到的是i從2-n進行遍歷,但怎麼判斷i是否被標記呢?
被標記的放到list或者dict,每次遇到i進行查詢
或者 建立乙個size=n陣列進行標記
查詢操作太慢, 實現會超時,這裡採用思路2,
1760 ms 25.3 mb, 超過7.20%
class solution:
def countprimes(self, n: int) ->int:
nums = [0] * n
res = 0
for i in range(2, n):
if nums[i] == 0:
mul = 2
res += 1
while mul * i < n:
nums[mul * i] += 1
mul += 1
return res
再進行一些小優化, while迴圈換成陣列操作
500 ms, 37.2 mb, 超過46.82%
class solution:
def countprimes(self, n: int) ->int:
nums = [0] * n
res = 0
for i in range(2, n):
if nums[i] == 0:
res += 1
nums[2*i : n : i] = [1]*len(nums[2*i : n : i])
return res
繼續優化, 主要是有乙個這樣的觀察:
假如當遍歷到5的時候,我們會對 5 * 2, 5 * 3, 5 * 4, 5 * 5, … 進行標記,但其實發現 5 * 2, 5 * 3, 5 * 4, 這些在之前已經被標記了,所以直接從 5 * 5進行標記, 即對於i * i開始遍歷; 另外, 當i超過sqrt(n)是, i * i就會超過 n,這時就不會改變標記。 所以只需要遍歷到 sqrt(n)。質數的個數就是 n - 被標記的個數。
204 ms, 37.1 mb, 超過77.81%
class solution:
def countprimes(self, n: int) ->int:
if n < 2:
return 0
nums = [0] * n
nums[1] = nums[0] = 1
for i in range(2, int(math.sqrt(n)) + 1):
if nums[i] == 0:
nums[i*i : n : i] = [1]*len(nums[i*i : n : i])
return n - sum(nums)
204 計數質數
統計所有小於非負整數 n 的質數的數量。示例 輸入 10 輸出 4 解釋 小於 10 的質數一共有 4 個,它們是 2,3,5,7 判斷質數的常規解法 如判斷n是否為質數,只需要判斷n是否能整除2 int sqrt n 厄拉多塞篩法 比如說求20以內質數的個數,首先0,1不是質數.2是第乙個質數,然...
204 計數質數
統計所有小於非負整數 n 的質數的數量。示例 輸入 10 輸出 4 解釋 小於 10 的質數一共有 4 個,它們是 2,3,5,7 埃拉託色尼篩選法,迴圈中置對應值的倍數為0,最後統計為1的個數,也就是質數的個數。class solution def countprimes self,n int i...
204 計數質數
題目 尋找 2,n 的質數是很常見的問題,主要有三種方法,現總結如下 列舉法思路簡單,但是要注意這幾點 1 0和1應該要特判,除非在判斷質數時壓根就不讓0,1參與選擇,直接從2開始 2 為了判斷n是否是質數,只需要列舉i 2至i 根號n,但是在程式中應該表現為i i n,素數條件如果從i i x改為...