AcWing 868 筛质数

题目描述:

给定一个正整数n,请你求出1~n中质数的个数。

输入格式

共一行,包含整数n。

输出格式

共一行,包含一个整数,表示1~n中质数的个数。

数据范围

1≤n≤10^6

输入样例:

8

输出样例:

4

分析:

方法一:埃式筛法

从2到n遍历所有的数,如果i是质数,就将i不超过n的倍数全部标记为合数。

内层循环会执行n / i次,也就是说,一共会执行n / 2 + n / 3 + ... + n / n = n * (1 / 2 + 1 / 3 + ... 1 / n) = O(nlogn),由调和级数的时间复杂度是lnn级别可推出上式。实际上,只有n为素数时,内层循环才会被执行,而由质数定理知,1到n中素数大概有n / lnn个,如果只是单纯的考虑质数占比是1 / lnn的话,得到的结论是该算法的时间复杂度是线性的。实际上,只是计算仅包含素数项的调和级数的和复杂度是loglogn的,因此,埃式筛法真实的时间复杂度是O(nloglogn),在n不是特别大时,该算法是接近于线性的。

#include <iostream>
using namespace std;
const int maxn = 1000005;
bool st[maxn];
int main(){
    int n,res = 0;
    cin>>n;
    for(int i = 2;i <= n;i++){
        if(st[i])   continue;
        res++;
        for(int j = i;j <= n;j += i)    st[j] = true;
    }
    cout<<res<<endl;
    return 0;
}

方法二:线性筛法

埃式筛法是将每个质数的倍数都筛一遍,比如6就会被2和3重复筛,这是没有必要的,我们可以采取线性筛法,规定每个数只会被其最小的质因子筛掉。

#include <iostream>
using namespace std;
const int maxn = 1000005;
bool st[maxn];
int primes[maxn];
int main(){
    int n,res = 0;
    cin>>n;
    for(int i = 2;i <= n;i++){
        if(!st[i])  primes[res++] = i;
        for(int j = 0;primes[j] <= n / i;j++){
            st[i * primes[j]] = true;
            if(!(i % primes[j]))    break;
        }
    }
    cout<<res<<endl;
    return 0;
}

可能上面的代码不易理解,不妨看下程序执行时筛掉的数。

 

分析上面的结果,i为质数时,会筛掉i和不超过它的质数乘积的数,比如2筛掉了4,3筛掉了6和9;i为合数时,会筛掉不超过i乘以不超过其最小质因子的质数乘积的数,比如4筛掉了8,6筛掉了12。那么每个质数的倍数是在何时被筛掉的呢?4是i = 2时筛掉的,6是i = 3时,乘上了素数表的最小质因子2筛掉的,8是i = 4时筛掉的,也就是说如果n是某质数x的倍数,则会在i = max(x,n / x)时被筛掉。对于质数情况,i = 2只会筛掉4,不会乘以大于它的质数3筛掉6,因此6只会在i = 3时被筛掉;对于合数情况,一旦i乘到了其最小质因子,就不再往上筛了。换而言之,与其说每个数只会被其最小质因子筛掉,不如说每个数只会被其最大因子筛掉。i是质数,则i的倍数中不小于i平方的数的最大因子一定是i;i是合数,则i的倍数中不小于i的k(k是i的最小质因子)倍的数的最大因子一定是i,比如12,最小质因子是2,不超过12的倍数24可以被筛掉,但是36的最大因子是18而不是12,所以此时还不能筛掉。

具体的说法,对于每个数x,p为其最小质因子,则p和x / p中的一个是x的最大因子,一个是x的最小质因子,当然,x为质数时,x的最大因子是等于最小质因子的,所以,被i的最大因子筛掉等同于被i的最小质因子筛掉。2可以筛掉n / 2个数,3可以筛掉n / 3 - n / 6个数,5筛掉n / 5个数...,最后总的复杂度是O(n)。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值