【力扣日记】204 计算质数 | 质数与质数相关问题

给出非负整数n,统计所有小于非负整数 n 的质数的数量。

算法:

通过质数的定义来计算一定会超时。评论区充斥着一种牛逼的筛法,如下:
计算小于非负整数n的质数:n<3时,return 0
n>=3时,2是第一个质数,其后所有2的倍数都不是质数,划去;
3是第二个质数,其后所有3的倍数都不是质数,划去;
4已排除,5是第三个质数,所有5的倍数都划去。
——————
算法如下:

class Solution:
    def countPrimes(self, n: int) -> int:
        if n<3:return 0
        ls=[1]*n
        ls[0],ls[1]=0,0#0与1不是质数
        for i in range(2,int(n**0.5)+1): 
            if ls[i] == 1:
                ls[i*i:n:i] = [0] * len(ls[i*i:n:i])
        return len([x for x in ls if x==1])

首先定义一个列表ls=[1]*nls[0],ls[1]=0,0因为0与1不是质数。
——————————

判断任意一个数n是不是质数,从质数的定义上来说,直觉的方法肯定是对数做循环,如果有除1和n本身外任一小于n的数可以被n整除,那么n就不是素数。

但是,如果它不是质数,那么它一定可以表示成两个数(除了1和它本身)相乘,这两个数必然有一个小于等于它的平方根。只要找到小于或等于的那个就行了。

所以算法上对质数的检验公式一般为

m = int(a ** 0.5)  # 根号n取整
i = 2
while i <= m:
	if a % i == 0:break  # n可以整除i
	i += 1
if i > m:print('%s是素数!' % a)
else:print('%s不是素数!' % a)

——————————
回到上面的算法:

        for i in range(2,int(n**0.5)+1): 
            if ls[i] == 1:
                ls[i*i:n:i] = [0] * len(ls[i*i:n:i])

对列表来说,索引就代表数字本身,1与0表示是否为质数。
如上,求小于非负整数n以内的质数,最后一个验证的应该是n-1.即for i in range(2,int((n-1)**0.5)+1)
遍历从索引为2开始,按步长为2切片,序列解包,依次将[0]赋值给所有对应索引位。
ls[i*i:n:i] = [0] * len(ls[i*i:n:i]),为何从ii开始赋值,举例从3开始,第一个被划去的应该是32,但是因为是2的倍数早已被划掉了,所已从33开始。若是11,112是2的倍数,113是3的倍数,……117是7的倍数,由此可知,对每个素数来说,开始赋值的起始位置应该是i*i。

最后需要返回所有小于非负整数 n 的质数的数量,即对列表ls进行遍历,统计有多少个1.
可以写为:len([x for x in ls if x==1]),使用列表筛选器
考虑到python中字符串的内置方法,还可以''.join(map(str,ls)).count('1'),但是这个方法本质上对ls进行3次遍历,非常慢,在此仅为多样性来提供,并不推荐。

——————————
小拓展。
ls的索引即数字本身,对return的表达式做修改即可返回所有小于n的质数集。
return [i for i,j in enumerate(ls) if j==1]

发布了70 篇原创文章 · 获赞 15 · 访问量 4318

猜你喜欢

转载自blog.csdn.net/Heart_for_Ling/article/details/103333643