数论常用内容——素数

来源:互联网 发布:windows获取时间戳 编辑:程序博客网 时间:2024/06/05 13:36

在做数论题过程中,素数出现的频率很高,在基础题和中档题甚至很多高难度的题里面都很常见,这篇博客就来对素数及其使用做一个小小的总结

素数

首先,我们应该明确,素数的定义:

素数(prime number)又称质数,有无限个。素数定义为在大于1的自然数中,除了1和它本身以外不再有其他约数的数称为素数。

素数个数估计

既然素数有无限多个,那么n以内的素数有多少个呢?

可以通过如下方式来估计n以内的素数个数

素数个数估计:π(n)≈x/ln(n)

筛素数

知道了有这么多的素数,那我们如何得到素数呢?

直接一个一个遍历判断显然是不可取的,在竞赛中,我们一般采用打素数表的方法来解决这个问题,不知道哪位高人给打素数表起了筛素数这个名字,个人感觉十分贴切。筛法有很多,这里我只介绍一下时间效率最高的欧拉筛法,请看代码

const int MAXN = 10000000 + 10;bool is_prime[MAXN];//素数筛,true表示该数为素数int prime[MAXN];//素数表,这里面存了MAXN以内的所有素数int tol;//素数个数统计void PrimeTable(){    //初始化    memset(is_prime,true,sizeof(is_prime));    tol=0;    //从最小的素数2开始筛    for(int i=2;i<MAXN;i++){        //如果是素数就加入素数表中        if(is_prime[i]) prime[tol++]=i;        //进行筛素数操作        for(int j=0;j<tol;j++){            if(i*prime[j]>MAXN)break;            //把当前数字与素数表中的每个数字的乘积全部筛掉(这些因为有其他因子了,所以不可能是素数)            is_prime[i*prime[j]]=false;            //这里很关键,它保证了每一个合数只被筛一遍(大家可以在纸上模拟一下这个过程,就会有深刻的理解)            if(i%prime[j]==0)break;        }    }}

素数的应用

大数据小区间筛素数个数

模拟筛法筛区间内素数个数,先打表,再根据表进行筛素数操作

typedef long long ll;const int MAXN = 1000000+10;bool is_prime[MAXN];//true的为合数,false的为素数int prime[MAXN/10];//存素数bool num[MAXN];int tol;void find_prime(){    memset(is_prime,false,sizeof(is_prime));    tol=0;    is_prime[1]=1;    for(int i=2; i<MAXN; i++){        if(!is_prime[i]){            prime[tol++]=i;            for(int j=i*2; j<MAXN; j+=i)                is_prime[j]=1;        }    }}int main(){//    freopen("in.txt","r",stdin);//    freopen("out.txt","w",stdout);    int T;    cin>>T;    find_prime();    for(int cas=1; cas<=T; cas++){        int a,b;        scanf("%d %d",&a,&b);        int n=b-a;        memset(num,false,sizeof(num));        for(int i=0;i<tol&&prime[i]*prime[i]<=b;i++){            int j=0;            if(a%prime[i]!=0)                j=prime[i]-a%prime[i];//找到第一个需要筛掉的数(j+a)%prime[i]==0            if(a<=prime[i])                j+=prime[i];//(j+a)/prime[i]==1,则(j+a)是素数,向下推一个            for(;j<=n;j+=prime[i])                num[j]=true;        }        int ans=0;        for(int j=0;j<=n;j++){            if(!num[j]) ans++;        }        if(a==1) ans--;        printf("Case %d: %d\n",cas,ans);    }    return 0;}

素因数分解

这里有两种素因数分解方法

首先,第一种朴素分解方法,这种方法适用于数字比较小的时候的因数分解

有两种不同的实现方式,大家可以任选其一

int a[MAXN/10];//保存因子int b[MAXN/10];//保存因子个数int tol2;void sbreak(ll n){    memset(a,0,sizeof(a));    memset(b,0,sizeof(b));    tol2=0;    for(int i=0; prime[i]*prime[i]<=n&&i<tol; i++)//此处的tol为筛出的素数总数{        if(n%prime[i]==0){            a[tol2]=prime[i];            while(n%prime[i]==0){                b[tol2]++;                n/=prime[i];            }            tol2++;        }    }    if(n!=1){        a[tol2]=n;        b[tol2++]=1;    }}

const int M = 1000010;int facs[M]; //n的所有质因子int tot; //n的质因子个数int kind; //质因子种数struct Factor{    int val; //该质因子的值    int num; //该质因子的个数}factor[M];void solve(int n){    tot=0;    for(int i=2;i*i<=n;i+=2){        while(!(n%i))            n/=i,facs[tot++]=i;        if(i==2) --i;    }    if(n>1) facs[tot++]=n;}void collect(int n){    solve(n);    kind=0;    factor[kind].val=facs[0];    factor[kind].num=1;    kind++;    for(int i=1;i<tot;i++){        if(facs[i]!=facs[i-1]){            factor[kind].val=facs[i];            factor[kind].num=1;            kind++;        }        else{            factor[kind-1].num++;        }    }}

第二种,Pollard_rho因数分解,这种方法适用于数字比较大时的因数分解

这种素数分解方法涉及MillerRabin素数测试的相关知识,大家可以到网上其他文章中了解一下相关知识,嫌麻烦也没关系,代码里有注释,大家也可以看代码直接理解

#include <ctime>#define LL long long#define case 10//一般8~10就够了using namespace std;LL Get_rand(LL n){    return 2 + rand()%(n-2);}LL Mul(LL a,LL b,LL m){    LL ans = 0;    while(b){        if(b & 1){            ans = (ans + a) % m;            b--;        }        else{            a = (2 * a) % m;            b >>= 1;        }    }    return ans%m;}LL Quick_mod(LL a,LL n,LL m){    LL ans = 1;    while(n){        if(n & 1){            ans = Mul(ans, a, m);        }        a = Mul(a, a, m);        n >>= 1;    }    return ans;}bool Miller_Rabbin(LL n){    if (n==2)return true;    if (n<2||!(n&1))return false;    int t=0;    LL a,x,y,u=n-1;    while((u&1)==0) t++,u>>=1;    for(int i=0;i<case;i++){        a=rand()%(n-1)+1;        x=Quick_mod(a,u,n);        for(int j=0;j<t;j++){            y=Mul(x,x,n);            if (y==1&&x!=1&&x!=n-1)                return false;            ///其中用到定理,如果对模n存在1的非平凡平方根,则n是合数。            ///如果一个数x满足方程x^2≡1 (mod n),但x不等于对模n来说1的两个‘平凡’平方根:1或-1,则x是对模n来说1的非平凡平方根            x=y;        }        if (x!=1)///根据费马小定理,若n是素数,有a^(n-1)≡1(mod n).因此n不可能是素数            return false;    }    return true;}long long factor[1000];//质因数分解结果(刚返回时是无序的)int tol;//质因数的个数。数组小标从0开始long long gcd(long long a,long long b){    if(a==0)return 1;//???????    if(a<0) return gcd(-a,b);    while(b){        long long t=a%b;        a=b;        b=t;    }    return a;}long long Pollard_rho(long long x,long long c){    long long i=1,k=2;    long long x0=rand()%x;    long long y=x0;    while(1){        i++;        x0=(Mul(x0,x0,x)+c)%x;        long long d=gcd(y-x0,x);        if(d!=1&&d!=x) return d;        if(y==x0) return x;        if(i==k){y=x0;k+=k;}    }}//对n进行素因子分解void findfac(long long n){    if(Miller_Rabbin(n))//素数{        factor[tol++]=n;        return;    }    long long p=n;    while(p>=n)p=Pollard_rho(p,rand()%(n-1)+1);    findfac(p);    findfac(n/p);}int main(){    int t;    LL n;    srand(time(NULL));    scanf("%d",&t);    while(t--){        scanf("%lld",&n);        if(Miller_Rabbin(n))            cout<<"Prime"<<endl;        else{            tol = 0;            findfac(n);            LL minn = 99999999999999999;            for(int i=0; i<tol; i++)                minn = min(minn,factor[i]);            cout<<minn<<endl;        }    }    return 0;}

求n的因数个数(是因数,不是素因数)

对n进行素因数分解,得到p1^a1+p2^a2+……+pi^ai则其因数个数为:(a1+1)*(a2+1)*……+(ai+1)

常见的素数的应用就是这些,欢迎大家留言补充

0 0
原创粉丝点击