题目
统计所有小于非负整数n的质数的数量。
思路
质数:除了1和这个数本身之外,不能被其他数整除。
注:1不是质数,因为1只有一个因子。
思路一:暴力法
遍历所有数num,再遍历所有num可能的因子。
时间复杂度:O(n^2)
class Solution:
def countPrimes(self, n: int) -> int:
if n <= 2: return 0
count = 1
for num in range(3, n):
factor = 2
while factor < num:
if num % factor == 0:
break
else:
factor += 1
if factor == num: count += 1
return count
思路二:优化暴力算法
细究,可以发现,假如一个数为9,那么其二分之一(4.5)后的数都已不用进行计算。
另外,所有偶数除2以外,都不可能为质数。
class Solution:
def countPrimes(self, n: int) -> int:
if n < 3: return 0
count = 1
for num in range(3, n):
if num & 1 == 0:
# 等同于 num % 2 == 0,判断n是否为偶数
continue
factor = 3
while factor * factor <= num:
if num % factor == 0:
break
else:
factor += 2 # 循环加2,规避偶数,因为已经判断过不是偶数了
if factor * factor > num: count += 1
return count
思路三:厄拉多塞筛法
如果我们在进行顺序遍历时,每取得一个数(排除0、1),如果将它所有的倍数(排除0、1、本身)都清除,那么,剩下的数是不是必为素数?
没错,这个有趣且实用的方法便是著名的厄拉多塞筛法!
- 选中数字2,并排除2的倍数;
- 选中数字3,并排除3的倍数;
- 选中数字4,并排除4的倍数;
……
class Solution:
def countPrimes(self, n: int) -> int:
# 厄拉多塞筛法
is_prime = [True] * (n + 2)
count = 0
for i in range(2, n):
if is_prime[i]:
count += 1
# 删除所有i的倍数的数,即相应位置的is_prime置为False
for j in range(i, n, i):
is_prime[j] = False
return count
抄一下别人优秀的代码
class Solution:
def countPrimes(self, n: int) -> int:
if n < 2: return 0
isPrimes = [1] * n
isPrimes[0] = isPrimes[1] = 0#设置0和1位为0
#下面的思路是在 2 到 根号n 的范围内,当一个数是质数,将它所有的比n小的倍数设置成0
for i in range(2, int(n ** 0.5) + 1):
if isPrimes[i] == 1:
#哇这个切片真的是pythonic
isPrimes[i * i: n: i] = [0] * len(isPrimes[i * i: n: i])
#现在每个质数位的flag为1,其余的位数为0.由于我们不需要知道质数是什么只要总数,因此直接返回list里面所有1的和就行。
return sum(isPrimes)