~(≧▽≦)/~啦啦啦
主要讲解三种常用的筛法及其原理
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; }