求0-n范围内的所有素数——三种解法

本文对比了朴素筛法的O(n√n)复杂度,Eratosthenes筛法的O(n log log n)改进,以及欧拉筛法的O(n)优化,详细讲解了如何避免重复计算,降低时间复杂度,实现更高效的素数查找。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

在这里插入图片描述

朴素筛法

朴素筛法就是一一验证1∼ n \sqrt{n } n 之内的数判断,时间复杂度O(n n \sqrt{n } n )。

int countPrimes(int n){
    int count=0;
    for(int i=2;i<n;i++){
        for(int j=2;j*j<=i;j++){//优化:对于每个数字i,只需要计算2~根号i范围内是否存在非质因子
            if(i% == 0)){//数字i存在因子j(非素数)
                break;
            }
            count++;
        }
    } 
    return count;
}

缺点:每次都需要与比根号i小的数进行计算(2~根号i),因此存在大量重复计算,时间复杂度较高。

Eratosthenes筛法

把素数i对应的倍数全部筛掉,时间复杂度O(n loglog n)。

int countPrimes(int n){
	//记录素数个数
    int count=0;
    //标记下标i对应的元素i是否为素数
    int isPrime[100010];
    //初始化2~n的元素均为素数
    for(int i=2;i<=n;i++)
        isPrime[i]=1;
    //0和1为非素数
    isPrime[0]=isPrime[1]=0;
    for(int i=2;i<=n;i++){
        if(isPrime[i]==1){//如果当前元素是素数,则将当前元素i的所有倍数均标记为非素数。当遇到非素数时,则不会进入循环,从而降低了时间复杂度。
            for(int j=i*2;j<=n;j+=i)
                isPrime[j]=0;
        }
    }
    //统计素数个数
    for(int i=1;i<=n;i++){
        if(isPrime[t]==1) {
        	count++;
        }
    }
    return count;
  
}

缺点:此算法仍存在部分重复计算,如数字6。43=12,26=12。那么在标记4倍数和标记6的倍数时都会对6进行标记,存在了重复标记的情况,增加了时间复杂度。

欧拉筛

欧拉筛的出现就是找到了唯一筛掉一个数的筛法,不会存在重复计算,所以时间复杂度是O(n)。

int countPrimes(int n){
	//统计素数个数
    int count=0;
    //标记当前元素i是否为非素数,1代表是非素数,0代表是素数
    int* record = malloc(n * sizeof(int));
    //存储所有找到的素数
    int* prime = malloc(n * sizeof(int));
    //初始化所有元素均为素数
    memset(record, 0, n * sizeof(int));
    for(int i=2;i<n;i++){
        if(!record[i]){//如果当前元素i是素数,存储当前元素
            prime[count++] = i;
        }
        //对于当前元素,将所有已经找到的素数与它的乘积标记为非素数
        for(int j=0;(j<count) && (i*prime[j]<n);j++){
            record[i*prime[j]] = 1;
            //判断当前元素是否是最小质因子
            //此步骤即保证了不会出现重复计算类似4*3=12和6*2=12的情况
            //执行4 *2 =8之后,4%prime[0] = 4%2 == 0就会跳出循环,不会执行4*3=12。因为4存在质因子2,则4不是12的最小质因子,所以跳出循环。
            //执行6*2=12时,会对12进行标记,然后跳出循环。(下一次循环所对应的6*3=18与9*2=18类似这种情况,所以在此跳出循环了,不会对其进行标记,而是在执行9*2=18的时候进行标记。)
            //综述来看,整体而言,保证了12会被标记为非素数,且只会标记一次。
            if(!(i%prime[j])){//i是素数的倍数
                break;
            }
        }
    } 
    free(prime);
    free(record);
    return count;
  
}

执行结果

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值