质数筛(线性筛的详细解释)

基本解释

所谓质数筛,也就是将某个区间内的质数个数统计出来,如果用基本的算法,无论怎样都会tle,这时就得借用古人的思想,所谓质数筛,也就是将在这个区间内的合数全部筛掉,剩下的就是质数了,而筛合数也是需要算法的,否则也会tle。。。。
三种算法,越往后效率越高:

朴素筛法:

void get_primes2(){
    for(int i=2;i<=n;i++){

        if(!st[i]) primes[cnt++]=i;
        for(int j=i;j<=n;j+=i){
            st[j]=true;
        }
    }

解释:说白了根据初中学的分解质因数,每个合数都由几个质因数相乘而得,那么我们只用把每个数的倍数筛掉即可,比如2的2倍,2的3倍,2的4倍等等,因为每个合数都可以拆成几个质数相乘,这里24一样可以拆成22*2,一样满足条件,只要把后面所有的数的倍数全筛掉,剩下的就是质数了。(tips:可以ac,但是时间为(nlogn),到1e8以上的数据时会tle)

埃氏筛法

void get_primes1(){
    for(int i=2;i<=n;i++){
        if(!st[i]){
            primes[cnt++]=i;
            for(int j=i;j<=n;j+=i) st[j]=true;
        }
    }
}

解释:跟上面算法很像,不过可以达到O(loglogn)的复杂度,上面的会把每个合数的倍数也筛掉,这样会重复筛很多已经筛过的数,这里就只用筛质数的倍数即可。

线性筛法:

void get_primes(){
    for(int i=2;i<=n;i++){
        if(!st[i]) primes[cnt++]=i;
        for(int j=0;primes[j]<=n/i;j++){
            st[primes[j]*i]=true; 
            if(i%primes[j]==0) break;
        }
    }
}

解释:之前都是筛去质数的倍数,这样虽然重复次数少很多了,但是一样还是会有重复,比如12,会被2筛掉,也会被3筛掉,这里的算法就巧妙地剔除了所有的重复机会:
1。primes数组用来存放质数,而第二重for循环用来筛去后面的合数,这里最巧妙地就是用每个合数的最小质因数去筛这些合数,为啥可以呢?且看下面。

假设一个数x有最大因数a,那么与其相乘的的a*b=x中的b一定是x的最小质因数,因为如果不是质数的话,为啥最小这个显而易见,而为啥是质数呢,因为如果不是质数,那么其为合数,合数可以拆成几个质数相乘,我们直接把多余的质数乘到那个目前最大的因数后会得到一个更大的因数和一个更小的质因数,这和原本我们的假设不符合,所以和最大因子相乘的那个数一定是它的最小质因数,所以每个合数都等于最大因子乘以最小质因数(最大因子可能和最小质因数相等,但不会小于,因为如果小于也会矛盾)。

再来看这里,在我们筛到一个数的最小质因数时,我们还不一定能筛到它的最大因子,所以在这里

 st[primes[j]*i]=true; 
 if(i%primes[j]==0) break;

在这里primes[j]无论如何都是primes[j]*i的最小质因数,

i%primes[j]!=0时,说明i的最小质因数大于primes[j],那么iprimes[j]的最小质因数是这俩的最小质因数中更小的那个,也就是prime[j]。
i%primes[j]==0时,不用多说,必然是最小质因数,而且这时候要break,否则下一个primes[j+1]并不会是i
primes[j]的最小质因数了,这里筛去的话就违背了我们一开始算法的本意了。

大概就这些。。。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值