求质数数量 了解线性筛(欧拉筛)

背景

Leetcode每日一题 204. 计数质数【简单】

  1. 看到题第一个想法就是判断每个数是否是质数=》方法一
  2. 当然最简单的想法会超时 然后就想到了如果某个数x是质数 那么x的倍数必定是合数可以不用判断
    后来查了资料知道了原来有个学名 埃氏筛【埃拉托斯特尼筛法】=》方法二
  3. 继续查看资料了解了 还有一种更加巧妙的方法线性筛 【欧拉筛】 =》方法三

代码

1. 普通判断

这里还对2的倍数进行了预处理 然而还是超时

def countPrimes(n):
    """
    :type n: int
    :rtype: int
    """
    import math
    def isPrime(num):
        if num==2 or num==3:
            return True
        if num==1 or num%2==0:
            return False
        mid = int(math.sqrt(num))+1
        for i in range(3,mid,2):
            if num%i==0:
                return False
        return True
    ret=0
    for i in range(2,n):
        if isPrime(i):
            ret+=1
    return ret
2. 埃氏筛

如果是质数 则将质数的倍数去除
大致流程:

i去除
24,6,8,10…
39,15,21…
4非质数已去除
525,35…
def countPrimes2(n):
    """
    :type n: int
    :rtype: int
    """
    num = set([i for i in range(2,n)])
    for i in range(2,n):
        if i in num:
            tmp=i*2
            while tmp < n:
                if tmp in num:
                    num.remove(tmp)
                tmp +=i
    return len(num)
3. 欧拉筛
def countPrimes3(n):
    """
    :type n: int
    :rtype: int
    """
    prime=[]
    isprime = [True]*n
    for i in range(2,n):
        if isprime[i]:
            prime.append(i)
        for p in prime:
            if p*i>=n:
                break
            isprime[p*i]=False
            if i%p==0:
                break
    return len(prime)

这里用prime来存储当前已判断的质数 从小到大
isprime用来记录是否是质数
代码好理解 最关键的就是

if i%p==0:break

这一条语句的目的是为了每一个合数只让最小质因数来判断其为合数
所以将判断次数变成了线性 并保证了不重复不遗漏
因为在prime中的质数是有序的 从小到大
即:prime[j]<prime[j+1]
当i可以被prime[j]整除时 我们可以假设 i= prime[j] * x
那么下一个需要判断的数
value=i*prime[j+1] => prime[j]*prime[j+1]*x
prime[j+1]并不是最小的能整除它的数 所以我们不需要继续判断prime[j]后面的质数与i相乘了
大致流程:

iprimeisprime变为false
2[2]4
3[2,3]6,9
4[2,3]8
5[2,3,5]10,15,25
6[2,3,5]12

看了流程 我们可以总结
思想还是一样 去除质数的倍数
实现方式与埃氏筛略有差别
埃氏筛:对于质数x 一次性去除所有x的倍数
而欧拉筛:对倍数i进行了遍历 每次去除当前所有质数的i倍

我们以流程中的12举例
当i=4时 我们先判断4*2=8 为合数 此时i%prime[j] 即4%2 余数为零
那么这时我们退出 不再判断 4*3=12 因为3并不是12的最小质因数
12的最小质因数为2
所以下面倍数i=6时 2*6=12 此时判断12为合数

由此可见 每一个合适有且仅有一次被判断为合数 与埃氏筛相比大大减少了判断次数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值