Leetcode 204:计数质数(超详细的解法!!!)

版权声明:本文为博主原创文章,未经博主允许不得转载。有事联系:[email protected] https://blog.csdn.net/qq_17550379/article/details/86062147

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

示例:

输入: 10
输出: 4
解释: 小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。

解题思路

这个问题非常简单。首先想到的通过暴力破解

class Solution:
    def countPrimes(self, n):
        """
        :type n: int
        :rtype: int
        """
        res = list()
        for i in range(2, n):
            if self.isPrime(i):
                res.append(i)
                
        return len(res)
        
    def isPrime(self, n):
        if n == 2:
            return True
        
        if n % 2 == 0:
            return False
        
        for i in range(3, int(pow(n, 0.5))+1):
            if n % i == 0:
                return False
            
        return True

超时了,只能换个方法。使用埃拉托色尼筛网法,给出要筛数值的范围n,找出 n \sqrt{n} 以内的素数。先用2去筛,即把2留下,把2的倍数剔除掉;再用下一个素数,也就是3筛,把3留下,把3的倍数剔除掉;接下去用下一个素数5筛,把5留下,把5的倍数剔除掉;不断重复下去…。

class Solution:
    def countPrimes(self, n):
        """
        :type n: int
        :rtype: int
        """       
        primes = [0] * n
        for i in range(2, n):            
            j = 2
            while i * j < n:
                primes[i*j] = 1
                j += 1
            
        return sum(primes)

依旧超时,我屮艸芔茻。接着对埃拉托色尼筛网法进行优化,优化思路也很简单,就是将原来的串行法改为并行法(也就是一次性将所有的标记标上)。

class Solution:
    def countPrimes(self, n):
        """
        :type n: int
        :rtype: int
        """
        if n < 2:
            return 0
        
        p = [1] * n
        for i in range(2, int(n ** 0.5) + 1):
            if p[i] == 1:
                p[i*i:n:i] = [0] * ((n - i*i - 1) // i + 1) # faster
                
        return sum(p) - 2

最后使用欧拉函数线性筛,主要要解决这样的问题:

我们发现在上面的筛法中有的数字是多个质数的倍数,也就是说它可能会被重复计算多次,比如说6同时是2与3的倍数,它在计算时就被访问了两次,这样会导致效率低下,所以欧拉筛针对这点进行了优化。

class Solution 
{
public:
    int countPrimes(int n) 
    {
        vector<int> primes(n, 0); //记录第i个质数
        deque<bool> flag(n, false);
        int count = 0;
        for (int i = 2; i < n; ++i)
        {
            if (!flag[i]) primes[++count] = i;
            for (int j = 1; j <= count and primes[j]*i <= n; ++j)
            {
                flag[primes[j]*i] = true;
                if (i % primes[j] == 0) break;
            }
        }    
        return count;
    }
};

但是这种做法在提交的时候只跑出来68ms的成绩。注意上面我没有使用数组,因为在windows下不支持可以变长数组,在新的标准中也移除这一特性。如果这里使用可以变长数组的话,速度会快上许多。

讲道理这道题目应该不能算是简单的题目吧。

reference:

https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes

https://blog.csdn.net/nixinyis/article/details/64131351?utm_source=blogxgwz8

https://leetcode.com/problems/count-primes/discuss/153528/Python3-99-112-ms-Explained%3A-The-Sieve-of-Eratosthenes-with-optimizations

https://stackoverflow.com/questions/16004407/a-fast-prime-number-sieve-in-python

https://www.cnblogs.com/BigYellowDog/p/9695648.html

我将该问题的其他语言版本添加到了我的GitHub Leetcode

如有问题,希望大家指出!!!

猜你喜欢

转载自blog.csdn.net/qq_17550379/article/details/86062147