欧拉筛法模板and 洛谷 P3383 【模板】线性筛素数(包括清北的一些方法)

题目描述

如题,给定一个范围N,你需要处理M个某数字是否为质数的询问(每个数字均在范围1-N内)

输入格式

第一行包含两个正整数N、M,分别表示查询的范围和查询的个数。

接下来M行每行包含一个不小于1且不大于N的整数,即询问该数是否为质数。

输出格式

输出包含M行,每行为Yes或No,即依次为每一个询问的结果。

当然这是一道很裸的板子题,但是却牵扯到了一个非常有用的东西:

素数筛法

首先,我们知道素数筛法主要就是以下几种

第一:
无脑筛

其实就是从2到n遍历一遍,没什么可讲的,顶多把n优化成sqrt(N),所是这种算法的时间复杂度就是O(sqrt(N))不可否定的是,这是已知判断单一数的最快而且是最精准的方法了,绝对不会出错

第二:

Miller-rabin素性测试

如果n为素数,取a<n,设n-1=d*2r,则要么ad≡1(mod n)要么存在0<=i<r,使得ad*2^t≡-1(mod n),要么存在0<=i<r,使得ad*2^t≡-1(mod n)(有可能都满足)

对于任意一个a,如果满足这两个条件,n有可能是质数,但a如果不满足这两个条件中的任何一个,它一定不是质数。找k个a,如果都满足这两个条件,k-1个“更”有可能是质数

 

在选取k的时候,最好选取2,3,5,7,13,29,37,89至少保证int范围内不会出错

因为筛法的不确定性来自于随机的a,但是当选取的数足够好,就没有问题;

 如果n是素数,取a<n,舍n-1=d*2r,则要么ad≡1(mod n),要么存在0<=i<r,使得a

部分代码:

int gg[8]={2,3,5,7,13,29,37,89};

long long kuaisumi(long long a,long long b1,long long c)
{
    long long i=a;
    while(b1)
    {
        if(b1&1) 
        {
            s=(s*i)%c;
        }
        i=(i*i)%c;
        b1>>=1;
    }
    return s%c;
}

bool miller_rabin(int a,int n)
{
    int d=n-1,r=0;
    while(d%2==0)
        d/=2,r++;
    int x=kuaisumi(a,d,n);
    if(x==1)return true;
    for(int i=0;i<r;i++)
    {
        if(x==n-1)return true ;
        x=(long long )x*x%n;
    }
    return false;//可以对照素性测试看 
}

bool is_prime (int n)
{
    if(n<=1)return false ;
    for(int a=0;a<8;a++)
        if(n==gg[a])return true;//一个个试 
    for(int a=0;a<8;a++)
        if(!miller_rabin(gg[a],n))return false;
    return true;
}

现在我们讲一下筛某一个范围的数的方法,

第三:

埃拉托色尼筛法(埃氏筛)

主要思想就是把所有质数的整倍数筛掉,速度比较快,也不难想,时间复杂度o(nloglogn)

代码实现:

#include<stdio.h>  
#include<math.h>
int main()  
{  int i,j,k;  
    int a[101];  
    for(i=1;i<=100;i++)   a[i]=i;    
    a[1]=0;               //先挖掉a[1]  
  
    for(i=2;i<sqrt(100);i++){  
        for(j=i+1;j<=100;j++){  
            if(a[i]!=0&&a[j]!=0){  
                if(a[j]%a[i]==0){  
                    a[j]=0;           //把非素数挖掉,不是素数的都赋值为0  
                }  
            }  
        }  
    }  
    printf("
"); 
    for(i=1,k=0;i<=100;i++){  
        if(a[i]!=0){              //选出值不为0的数  即素数   
            //cout<<" "<<a[i];  
            printf("%d ",a[i]);
            k++;  
        }  
  
        if(k==10){      
            printf("
"); 
            k=0;  
        }  
    }  
    printf("
"); 
  
    return 0;  
}

还有一个是对埃氏筛的优化,叫做欧拉筛,也叫作线性筛

代码实现:

#include<cstdio>
#include<iostream>
using namespace std;
int tot,n,b[9999999],pri[9999999];//b[i]存的是i是否为质数,0为质数,1为合数;pri[i]存的是第i个质数
inline void shai()
{
  //b[1]=1; 
    for(int i=2;i<=n;i++)//从2到n,因为1不是质数可以跳过,当然有的时候可能会用到b[1],这个时候需要赋特值如上
    {
        if(!b[i])pri[++tot]=i;
        for(int j=1;j<=tot&&i*pri[j]<=n;++j)
        {
            b[i*pri[j]]=1;
            if(i%pri[j]==0)break;//如果i%pri[j]==0,就说明是i的倍数的数一定是某质数的倍数,这个时候就可以把它去掉从而节省时间
        }
    }
}
int main()
{
    cin>>n;
    shai();
    for(int i=1;i<=tot;i++)
    {
        cout<<pri[i]<<endl;//这里是输出n以内的质数,如果想判断一个数是否为质数可以看b[i]
    }
}

所有的筛法就讲完了,最后我们贴一下AC代码:

#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
int tot,n,m,b[9999999],pri[9999999],a[9999999];
inline void shai()
{
    for(int i=2;i<=n;i++)
    {
        if(!b[i])pri[++tot]=i;
        for(int j=1;j<=tot&&i*pri[j]<=n;++j)
        {
            b[i*pri[j]]=1;
            if(i%pri[j]==0)break;
        }
    }
}
int main()
{
    cin>>n>>m;
    shai();
    for(int i=1;i<=m;i++)
    {
        cin>>a[i];
    }
    for(int i=1;i<=m;i++)
    {
        if(a[i]==1)
        {
            cout<<"No"<<endl;
        }
        else
        {
        if(b[a[i]]==0)
            cout<<"Yes"<<endl;
        else
            cout<<"No"<<endl;
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/this-is-M/p/10672934.html