素数问题——Meisell-Lehmer算法
来源:互联网 发布:什么是linux内核 编辑:程序博客网 时间:2024/05/22 04:42
我觉得素数问题不是一两篇文章就可以解释清楚的,在数论中很多相关问题至今都无法解决。这里只是简单的总结在竞赛中用到的一小部分。
HDU5901这道是网络赛的一道素数题,有多种解法,这里推荐一些大佬的博客:http://blog.csdn.net/chaiwenjun000/article/details/52589457
http://blog.csdn.net/angon823/article/details/52577145(有资料链接)
素数筛有很多种方法,比如欧拉筛,埃氏筛,Meisell-Lehmer算法等等
这里主要说一下Meisell-Lehmer算法
Meisell-Lehmer算法模板(计算2~n中的素数个数)
bool np[maxn]; int prime[maxn],pi[maxn]; int getprime() { int cnt=0; np[0]=np[1]=true; pi[0]=pi[1]=0; for(int i=2; i<maxn; ++i) { if(!np[i]) prime[++cnt]=i; pi[i]=cnt; for(int j=1; j<=cnt&&i*prime[j]<maxn; ++j) { np[i*prime[j]]=true; if(i%prime[j]==0) break; } } return cnt; } const int M=7; const int PM=2*3*5*7*11*13*17; int phi[PM+1][M+1],sz[M+1]; void init() { getprime(); sz[0]=1; for(int i=0; i<=PM; ++i) phi[i][0]=i; for(int i=1; i<=M; ++i) { sz[i]=prime[i]*sz[i-1]; for(int j=1; j<=PM; ++j) { phi[j][i]=phi[j][i-1]-phi[j/prime[i]][i-1]; } } } int sqrt2(ll x) { ll r=(ll)sqrt(x-0.1); while(r*r<=x) ++r; return int(r-1); } int sqrt3(ll x) { ll r=(ll)cbrt(x-0.1); while(r*r*r<=x) ++r; return int(r-1); } ll getphi(ll x,int s) { if(s==0) return x; if(s<=M) return phi[x%sz[s]][s]+(x/sz[s])*phi[sz[s]][s]; if(x<=prime[s]*prime[s]) return pi[x]-s+1; if(x<=prime[s]*prime[s]*prime[s]&&x<maxn) { int s2x=pi[sqrt2(x)]; ll ans=pi[x]-(s2x+s-2)*(s2x-s+1)/2; for(int i=s+1; i<=s2x; ++i) { ans+=pi[x/prime[i]]; } return ans; } return getphi(x,s-1)-getphi(x/prime[s],s-1); } ll getpi(ll x) { if(x<maxn) return pi[x]; ll ans=getphi(x,pi[sqrt3(x)])+pi[sqrt3(x)]-1; for(int i=pi[sqrt3(x)]+1,ed=pi[sqrt2(x)]; i<=ed; ++i) { ans-=getpi(x/prime[i])-i+1; } return ans; } ll lehmer_pi(ll x) { if(x<maxn) return pi[x]; int a=(int)lehmer_pi(sqrt2(sqrt2(x))); int b=(int)lehmer_pi(sqrt2(x)); int c=(int)lehmer_pi(sqrt3(x)); ll sum=getphi(x,a)+ll(b+a-2)*(b-a+1)/2; for(int i=a+1; i<=b; i++) { ll w=x/prime[i]; sum-=lehmer_pi(w); if(i>c) continue; ll lim=lehmer_pi(sqrt2(w)); for(int j=i; j<=lim; j++) { sum-=lehmer_pi(w/prime[j])-(j-1); } } return sum; }
这种算法在时间和数据量上有很大优势,但是在内存上有一定的不足
改模板的时候,原来的七个数可以取前五个,这里会消耗一些内存
在之江学院的预赛中出了卡内存和时间的素数题,当时过这个题要优化模板还得加上二分
这里有必要研究一下
题目链接http://115.231.222.240:8081/JudgeOnline/problem.php?id=1489
米勒罗宾算法主要是求从1到n内有多少素数的,所以可以用二分优化,上限是估算的最大值
代码:
#include <iostream>#include <cstdio>#include <cstring>#include <cmath>using namespace std;typedef long long ll;const int maxn= 10005;bool np[maxn];int prime[maxn],pi[maxn];int getprime(){ int cnt=0; np[0]=np[1]=true; pi[0]=pi[1]=0; for(int i=2; i<maxn; ++i) { if(!np[i]) prime[++cnt]=i; pi[i]=cnt; for(int j=1; j<=cnt&&i*prime[j]<maxn; ++j) { np[i*prime[j]]=true; if(i%prime[j]==0) break; } } return cnt;}const int M=5;const int PM=2*3*5*7*11;int phi[PM+1][M+1],sz[M+1];void init(){ getprime(); sz[0]=1; for(int i=0; i<=PM; ++i) phi[i][0]=i; for(int i=1; i<=M; ++i) { sz[i]=prime[i]*sz[i-1]; for(int j=1; j<=PM; ++j) { phi[j][i]=phi[j][i-1]-phi[j/prime[i]][i-1]; } }}int sqrt2(ll x){ ll r=(ll)sqrt(x-0.1); while(r*r<=x) ++r; return int(r-1);}int sqrt3(ll x){ ll r=(ll)cbrt(x-0.1); while(r*r*r<=x) ++r; return int(r-1);}ll getphi(ll x,int s){ if(s==0) return x; if(s<=M) return phi[x%sz[s]][s]+(x/sz[s])*phi[sz[s]][s]; if(x<=prime[s]*prime[s]) return pi[x]-s+1; if(x<=prime[s]*prime[s]*prime[s]&&x<maxn) { int s2x=pi[sqrt2(x)]; ll ans=pi[x]-(s2x+s-2)*(s2x-s+1)/2; for(int i=s+1; i<=s2x; ++i) { ans+=pi[x/prime[i]]; } return ans; } return getphi(x,s-1)-getphi(x/prime[s],s-1);}ll getpi(ll x){ if(x<maxn) return pi[x]; ll ans=getphi(x,pi[sqrt3(x)])+pi[sqrt3(x)]-1; for(int i=pi[sqrt3(x)]+1,ed=pi[sqrt2(x)]; i<=ed; ++i) { ans-=getpi(x/prime[i])-i+1; } return ans;}ll lehmer_pi(ll x){ if(x<maxn) return pi[x]; int a=(int)lehmer_pi(sqrt2(sqrt2(x))); int b=(int)lehmer_pi(sqrt2(x)); int c=(int)lehmer_pi(sqrt3(x)); ll sum=getphi(x,a)+ll(b+a-2)*(b-a+1)/2; for(int i=a+1; i<=b; i++) { ll w=x/prime[i]; sum-=lehmer_pi(w); if(i>c) continue; ll lim=lehmer_pi(sqrt2(w)); for(int j=i; j<=lim; j++) { sum-=lehmer_pi(w/prime[j])-(j-1); } } return sum;}int main(){ init(); ll n; int cas=1; while(~scanf("%lld",&n)&&n) { ll l=2,r=5e7+10; while(l<r){ ll m=(l+r)/2; ll res=lehmer_pi(m); if(res>=n) r=m; else l=m+1; } printf("Case %d: %lld\n",cas++,l); } return 0;}
说明:这个题内存限制在16M,但是数据是在三百万之内的,所以可以对模板进行阉割的。杭电上的求10的11次方的素数内存是512M。主要可以通过这个题学会在用二分进行优化的方法。这个模板处理大范围数据还是很有用的
阅读全文
0 0
- 素数问题——Meisell-Lehmer算法
- Meisell-Lehmer算法(大素数模板)
- 1000亿以内的素数统计(Meisell-Lehmer算法)
- Meisell-Lehmer算法(求1...n范围内的素数个数)
- 1000亿以内的素数统计(Meisell-Lehmer算法)
- HDU5901 Count primes(Meisell-Lehmer算法+模板)
- Meisell-Lehmer(1000亿以内的素数统计)(模板)
- HDU 5901 Count primes (Meisell-Lehmer算法 -- 模板)
- ZJCOJ L先生与质数V3/V4 (Meisell-Lehmer算法)
- ZJCOJ L先生与质数V3/V4 (Meisell-Lehmer算法)
- 【模板】Meisell-Lehmer 模板
- hdu 5901 Count primes(Meisell-Lehmer 统计n(很大)以内的素数个数)
- [hdu5901 Count primes]Meisell-Lehmer求质数个数PI(X)
- c++求素数个数的几种算法(普通筛、线性筛、Meisell-Lemher模板)
- 【HDU】5901】【模板题】Count primes 【Meisell-Lehmer求质数个数】
- NEFU120 梅森素数【Lucas-Lehmer判定】
- 0049算法笔记——【随机化算法】蒙特卡罗算法,主元素问题,素数测试问题
- 0049算法笔记——【随机化算法】蒙特卡罗算法,主元素问题,素数测试问题
- java学习路线图
- Knox的安装与部署(五)
- 为什么zookeeper集群中节点配置个数是奇数个?
- Tensorflow一些常用基本概念与函数(1)
- git文件没有绿色红色小图标
- 素数问题——Meisell-Lehmer算法
- POJ
- JavaScript的事件对象
- liunx命令
- 棋牌APP从开发到运营,要在哪些地方花钱?
- mavenSSM整合实现HelloWorld
- 搭建DHCP服务器
- 题目243-交换输出
- Scrapy模拟表单登录