计数质数

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

示例:

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

 

class Solution {
public:
    int countPrimes(int n) 
    {   
        bool *isPrime = new bool[n];
        for (int i = 2; i < n; ++i) {
            isPrime[i] = true;
        }

        /*两种方法
        for (int i = 2; i*i < n; ++i) 
        {
            if (!isPrime[i]) continue;
            for (int j = i*i; j < n; j += i) 
            {
                isPrime[j] = false;
            }
        }
        */

        for(int i=2;i*i<=n;i++){
            if(isPrime[i]==false)continue;
            for(int j=2;j*i<n;j++){
                isPrime[i*j]=false;
            }
        }

        int count = 0;
        for (int i = 2; i < n; ++i) {
            if (isPrime[i]) count++;
        }
        return count;
    }
    bool isPrime(int num) 
    {
        if (num <= 1) return false;
        for (int i = 2; i*i <= num; ++i) 
        {
            if (num%i == 0) return false;
        }
        return true;
    }
};

 

<think>好的,我现在要解决用户的问题,关于如何计算质数的方法或算法。首先,我需要回顾一下质数的定义:质数是大于1的自然数,除了1和它本身之外没有其他因数。所以计算质数通常指的是找出小于或等于某个数n的所有质数。 用户提到的是“计数质数”,也就是统计小于n的质数的数量。常见的算法有哪些呢?我记得最基础的方法可能是暴力法,对每个数检查是否能被小于它的数整除。但这种方法效率很低,特别是当n很大的时候,时间复杂度是O(n^2),显然不适用。 然后,应该想到埃拉托斯特尼筛法(Sieve of Eratosthenes),这是一个比较古老但高效的算法。它的基本思想是从小到大遍历,每次遇到一个质数,就标记它的所有倍数为非质数。这样剩下的未被标记的就是质数了。这个算法的时间复杂度是O(n log log n),效率比暴力法高很多。 不过,埃氏筛法在实现的时候可能需要优化空间复杂度,因为当n很大时,用一个数组来标记所有数可能会占用较多内存。例如,当n是10^6时,需要大约1MB的内存(假设用位存储的话),但如果是更大的n,比如10^9,这显然不可行。这时候可能需要考虑其他方法,比如分段筛法,但用户的问题可能更倾向于常见的实现,所以先考虑标准的埃氏筛法。 另一个可能的算法是欧拉筛法(线性筛),它能在O(n)的时间复杂度内完成,因为它确保每个合数只被标记一次。不过,实现起来可能稍微复杂一些,需要记录每个数的最小质因数,并在遇到其最小质因数时停止标记。不过对于一般的计数质数问题,埃氏筛法已经足够高效,尤其是当n不是特别大的时候。 现在,我需要把这些思路整理成算法步骤,并给出代码实现。首先,埃氏筛法的步骤大致如下: 1. 创建一个布尔数组is_prime,初始化为True,长度为n,其中is_prime[i]表示i是否为质数。 2. 将0和1的位置设为False,因为它们不是质数。 3. 从2开始遍历到sqrt(n),如果当前数i是质数,那么将i的所有倍数标记为非质数。 4. 最后统计is_prime中为True的数量。 不过,在具体实现时,可以优化标记倍数的过程。例如,当i是质数时,从i*i开始标记,步长为i,这样可以减少重复标记,因为更小的倍数已经被之前的质数处理过了。比如,当i=2时,标记4、6、8...;当i=3时,从9开始标记,因为6已经被i=2处理了。 接下来,代码实现的话,可以用Python。例如,用户提供的示例中的快速排序用了递归和列表生成式,类似的结构可能适合筛法的实现。需要注意的是,当n小于2时,直接返回0,因为没有质数。否则,初始化数组,然后遍历。 可能的Python代码: def count_primes(n): if n <= 2: return 0 is_prime = [True] * n is_prime[0] = is_prime[1] = False for i in range(2, int(n**0.5)+1): if is_prime[i]: is_prime[i*i:n:i] = [False]*len(is_prime[i*i:n:i]) return sum(is_prime) 不过,这里需要注意,在Python中,对于较大的n,这样的数组可能会占用较多内存。例如,当n是10^7时,数组的大小是10^7,每个元素是布尔值,大约需要1MB左右(每个布尔值在Python中实际占用更多空间,但这里用列表的话可能更高效的是使用位数组,比如用bitarray库,但标准实现可能还是用列表)。不过对于一般的题目要求,这样的实现已经可以处理较大的n了。 然后,需要考虑一些优化点。例如,在标记倍数时,初始的i只需要遍历到sqrt(n),因为如果一个数n有因数,那么其中一个因数必然小于等于sqrt(n)。所以这样可以减少循环次数。 另外,统计的时候直接求和即可,因为True会被视为1,False视为0。 测试这个函数的话,比如n=10,应该返回4(质数为2,3,5,7)。或者n=2,返回0;n=3,返回1(2)等。 现在,用户可能需要更详细的解释,比如算法步骤、复杂度分析,以及可能的优化方法。例如,埃氏筛法的空间优化可以用位运算来压缩存储,或者用生成器逐步处理,但可能超出一般实现的范畴。 此外,用户的问题可能还涉及到其他方法,比如米勒-拉宾素性测试(Miller-Rabin primality test)用于大数的判断,但这种方法通常用于单个大数的质数判断,而不是批量计算所有小于n的质数。所以对于计数质数的问题,埃氏筛法更合适。 总结来说,用户的问题需要给出埃氏筛法的实现,并解释其原理和复杂度,可能还要提到其他方法如线性筛法,但以埃氏为主。</think>### 计数质数的算法实现 质数是大于1的自然数,且除了1和自身外没有其他因数。计算质数的数量通常使用高效的筛法,例如**埃拉托斯特尼筛法**(Sieve of Eratosthenes)[^1]。 #### 埃拉托斯特尼筛法步骤 1. 初始化长度为$n$的布尔数组`is_prime`,标记所有数为质数(`True`)。 2. 将`0`和`1`标记为非质数(`False`)。 3. 从$i=2$遍历到$\sqrt{n}$,若$i$是质数,则将其所有倍数标记为非质数。 4. 统计数组中`True`的数量。 #### Python实现 ```python def count_primes(n): if n <= 2: return 0 is_prime = [True] * n is_prime[0] = is_prime[1] = False for i in range(2, int(n**0.5) + 1): if is_prime[i]: is_prime[i*i : n : i] = [False] * len(is_prime[i*i : n : i]) return sum(is_prime) ``` **时间复杂度**:$O(n \log \log n)$ **空间复杂度**:$O(n)$ #### 优化方法 1. **空间压缩**:使用位运算(如`bitarray`库)减少内存占用。 2. **分段筛法**:处理极大$n$时,分块加载数据以减少内存压力[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值