素数问题——Meisell-Lehmer算法

我觉得素数问题不是一两篇文章就可以解释清楚的,在数论中很多相关问题至今都无法解决。这里只是简单的总结在竞赛中用到的一小部分。

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。主要可以通过这个题学会在用二分进行优化的方法。这个模板处理大范围数据还是很有用的


  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值