线性筛法(伪模板及。。。)

~(≧▽≦)/~啦啦啦

主要讲解三种常用的筛法及其原理

1.线性筛法:改进了埃氏筛法(埃拉托斯特尼筛法),保证了每一个数只被它最小的质因数筛去,避免了埃氏筛法的重复运行

int check[N],prime[N],tot;
void OULA(){
    for(int i=2;i<=n/2;i++){
        if(!check[i]){
            prime[tot++]=i;
        }for(int j=0;j<tot;j++){
            if(i*prime[j]>n) break;//判断越界
            check[i*prime[j]]=1;
            if(i%prime[j]==0) break;//保证最优
        }
    }
}

2.6倍原理

题解 P3383 【【模板】线性筛素数】可以直接转到这个大佬博客阅读

一个结论是大于5的质数一定出现在6的倍数的两侧

大于5的数可以写成如下格式$6x-1,6x,6x+1,6x+2,6x+3,6x+4,6x+5,6(x+1),6(x+1)+1...$

可以发现不是$6$的倍数的两侧的数都可以写成如下三种格式中的一种

$2(3x+1),3(2x+1),2(3x+2)$,显然这些数都不是质数,然后再除去$6x$本身

根据以上规律,判断质数可以以6个数为单元快进

bool pd(int x){
    if(x==1) return 0;
    if(x==2||x==3) return 1;
    if(x%6!=1&&x%6!=5) return 0;
    int tmp=sqrt(x);
    for(int i=5;i<=tmp;i+=6)
        if(x%i==0||x%(i+2)==0) return 0;
    return 1;
}

3.Miller Rabin算法

%%%大佬博客%%%

总结要点,那么长的话就这么点儿东西

1.费马小定理$a^{p-1}≡1(mod p)$,在$p$是质数的前提下,证明略

满足$a^{p-1}≡1(mod p)$的数$p$不一定是素数,反例是卡迈克尔数

2.二次探测定理

若$p$为素数,$a^2≡1(modP)$,那么$a≡±1(modP)$

证明没有看懂。。。咕咕咕

3.算法流程

将$p-1$分成$2^k+t$,当$p$是素数,$a^{2^k+t}≡1(mod p)$

然后随机选择一个数,计算出$a^t$,让其不断自乘,然后结合二次探测定理判断

自乘后的数$(mod p)=1$,但之前的数$(mod p)≠±1$,这个数就是合数

若$p$通过一次测试,则$p$不是素数的概率为$25$%

那么经过$t$轮测试,$p$不是素数的概率为$frac{1}{4^t}$

我习惯用$2,3,5,7,11,13,17,19$这几个数进行判断

虽然是基于概率的运算,但失败率几乎为0

int n,m,test[10]={2,3,5,7,11,13,17};
int pow(int a,int b,int mod){
    int s=1;
    for(;b;b>>=1,a=1ll*a*a%mod)
        if(b&1) s=1ll*s*a%mod;
    return s%mod;
}
bool pd(int P){
    if(P==1) return 0;
    int t=P-1,k=0;
    while(!(t&1)) k++,t>>=1;
    for(int i=0;i<4;i++){
        if(P==test[i]) return 1;
        int a=pow(test[i],t,P),nxt=a;
        for(int j=1;j<=k;j++){
            nxt=1ll*a*a%P;
            if(nxt==1&&a!=1&&a!=P-1) return 0;
            a=nxt;
        }
        if(a!=1) return 0;
    }
    return 1;
}

P3383 【模板】线性筛素数

模板题

P1835 素数密度_NOI导刊2011提高(04)

区间筛法模板,大体思路是:筛出$sqrt(r)$内的素数,然后用这些素数去筛选$[l,r]$中的数

#include<iostream>
#include<cstdio>
#include<cmath>

#define N 10000005
#define LL long long 
using namespace std;

bool is_prime[N],is_prime_small[N];
void segment_sieve(LL l,LL r){
    for(LL i=0;i*i<=r;i++)
        is_prime_small[i]=true;
    for(LL i=0;i<=r-l;i++)
        is_prime[i]=true;
    
    for(LL i=2;i*i<=r;i++){
        if(is_prime_small[i]){
            for(LL j=2*i;j*j<=r;j+=i)
                is_prime_small[j]=false;
            for(LL j=max(2LL,(l+i-1)/i)*i;j<=r;j+=i)//保证j位于区间内
                is_prime[j-l]=false;
        }
    }
}

LL l,r,ans;

int main()
{
    scanf("%lld%lld",&l,&r);
    segment_sieve(l,r);
    for(int i=0;i<=r-l;i++) if(is_prime[i]) ++ans; 
    
    printf("%lld
",ans);
    
    return 0;
} 

线性筛法的写法,怎么感觉一样呢?

#include<iostream>
#include<cstdio>
#include<cmath>

#define N 5000000
using namespace std;

int prime[N],tot,ans;
bool vis[N],ins[N];

inline void OULA(int n){
    for(int i=2;i<=n;i++){
        if(!vis[i]) prime[++tot]=i;
        for(int j=1;j<=tot;j++){
            if(i*prime[j]>n) break;
            vis[prime[j]*i]=true;
            if(i%prime[j]==0) break;
        }
    }
}

int l,r;

int main()
{
    
    scanf("%d%d",&l,&r);
    
    OULA((int)ceil(sqrt(r)));
    
    for(int i=1;i<=tot;i++){
        if(prime[i]>r) break;
        int L=ceil(l*1.0/prime[i]),R=ceil(r*1.0/prime[i]);
        if(L==1) L=2;
        for(int j=L;j<=R&&prime[i]*j<=r;j++)
            ins[prime[i]*j-l+1]=1;
    }
    
    for(int i=1;i<=r-l+1;i++)
        if(!ins[i]) ++ans;
    
    printf("%d
",ans);
    return 0;
}
线性筛法
原文地址:https://www.cnblogs.com/song-/p/9764671.html