质数:2021-07-23质数定义为在大于1的自然数中,除了1和它本身以外不再有其他因数。

def countPrimes(self, n: int) -> int:
    def is_primes(c):
        """
        判断c是否为质数
        """
        for i in range(2, int(pow(c, 0.5)) + 1):
            if c % i == 0:
                return False
        return True

    ans = 0
    for j in range(2, n):
        if is_primes(j):
            ans += 1
    return ans

"""
解法三(更不暴力的暴力算法):

【思路】

在解法二的基础上,我们发现其实如果已经除了2,那么再除4、6等偶数都是没有意义的。

因此,我们将算法优化为只除以之前已经发现的质数,得到如下算法:
"""

def countPrimes(self, n: int) -> int:
    prime_list = []
    for c in range(2, n):
        for i in prime_list:
            if c % i == 0:
                break
        else:
            prime_list.append(c)
    return len(prime_list)
"""
解法四(使用数组标记合数):

【思路】

既然后面的数我们已经是通过之前除以之前发现的质数来判断是否为质数,那没有其实已经没有必要再继续使用耗时更高除法了。

我们可以建立一个数组,用以标记各个数是否为质数;当我们每发现一个质数,就遍历将所有该质数的倍数标记为合数;这种方法叫做“厄拉多塞筛法”。

由此,我们得到如下算法,虽然时间复杂度为O(n^2),但是不用再使用除法了:
"""

def countPrimes(self, n: int) -> int:
    num_list = [True for _ in range(n)]

    for i in range(2, n):
        if num_list[i]:  # 如果i为质数(不是任何质数的倍数)
            for j in range(2 * i, n, i):
                num_list[j] = False

    ans = 0
    for i in range(2, n):
        if num_list[i]:
            ans += 1
    return ans
"""
解法五(优化解法四):

【思路】

因为我们在标记7的倍数时,14、21、28、35、42都已经被2、3、5标记过了,即7*7以下的7的倍数都已经被标记了,所以,我们只需要从7*7向上标记7的倍数就可以了。

因此,当我们判断到n平方根(即标记完小于等于n平方根的最大质数)后,就不用再判断和标记了。

由此,我们得到了如下算法:
"""

def countPrimes(self, n: int) -> int:
    num_list = [True for _ in range(n)]

    for i in range(2, int(pow(n, 0.5)) + 1):
        if num_list[i]:  # 如果i为质数(不是任何质数的倍数)
            for j in range(i * i, n, i):
                num_list[j] = False

    ans = 0
    for i in range(2, n):
        if num_list[i]:
            ans += 1
    return ans

#解法六(优化解法五):

#【思路】

#因为在Python中,Python的内置语句往往会比我们自己写的算法要更快,因此我们更多地使用一些Python的#内置语句来优化算法。

#同时,在生成各元素相同的列表时,“列表生成式”的运行速度比“列表乘以常数”的运行速度要慢很多,不再使#用列表生成式。


def countPrimes(self, n: int) -> int:
    if n < 2:
        return 0

    num_list = [True]*n
    num_list[0], num_list[1] = False, False

    for i in range(2, int(pow(n, 0.5)) + 1):
        if num_list[i]:  # 如果i为质数(不是任何质数的倍数)
            num_list[i * i::i] = [False] * ((n - i * i - 1) // i + 1)  # 因为要包含i*i所以需要+1;因为n不在列表里,所以需要-1

    return sum(num_list)  # True就是1,False就是0,可以直接统计

解题思路
注意 题目说的是小于n的所有数字中质数的个数 所以是 is_prime = [True]*(n)
表示我们考虑is_prime[1],...,is_prime[n-1]

我们从2向后遍历,每遇到一个数字,将其倍数所对应的 is_prime 设为False,
因此遇到新的数字num,is_prime[num]=True说明它不是任何2..num-1的数字的倍数,即质数。

代码

def countPrimes(n):
    is_prime = [True]*(n)
    ans = 0
    for num in range(2,n): 
        if is_prime[num]:
            ans+=1
            # 右边界:因为数字最大是n-1 所以只需要到(n-1)//num 右边是开区间 所以+1
            for k in range(1,(n-1)//num+1):
                is_prime[num*k]=False
    return ans


作者:yuer-flyfly
链接:https://leetcode-cn.com/problems/count-primes/solution/ji-shu-zhi-shu-yi-ci-bian-li-python-by-y-dnqr/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

zhuanfa参考:https://leetcode-cn.com/problems/count-primes/solution/xiao-bai-chang-shi-de-6chong-fang-fa-by-changxingj/ 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落雪snowflake

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值