素数判定与素数筛

稚嫩判定

素数判定很早我们就了解了,是时候回味下当时青涩的代码了。

bool isprime(int x){
    int MAX=sqrt(x)+0.1;
    if(x<2)return false;
    for(int i=2;i<=MAX;i++){
        if(x%i==0)return false;
    }
    return true;
}

普通筛

然而当我们大量的需要素数判定的时候这样就需要打表了,这样就需要素数筛了!
普通筛:

int prime[10000000],primesize;
bool isprime[10000000];
void get_prime(int size){
    memset(isprime,true,sizeof isprime);
    isprime[0]=isprime[1]=false;
    for(int i=2;i<=size;i++){
        if(!isprime[i])continue;
        prime[primesize++]=i;
        for(int j=2*i;j<=size;j+=i)isprime[j]=false;
    }
}

欧拉筛

但是我们发现这样其实浪费了好多时间,比如6他竟然被筛掉了两次,哎,浪费啊。
那我们想,如果每个数只能被最小质因子筛掉就好了。我们知道任何数都是素数的乘积,那我们把素数乘积筛掉不就好了吗?那怎么保证最小素数筛掉呢?if(i%prime[j]==0)break;就可以了。

int prime[10000000],primesize;
bool isprime[10000000];
void get_prime(int size){
    memset(isprime,true,sizeof isprime);
    isprime[0]=isprime[1]=false;
    for(int i=2;i<=size;i++){
        if(isprime[i])prime[primesize++]=i;
        for(int j=0;j<primesize&&i*prime[j]<=size;j++){
            isprime[i*prime[j]]=false;
            if(i%prime[j]==0)break;
        }
    }
}

顺便提一下,欧拉函数phi的一些性质,phi[x]表示小于x并且与x互质的数的个数是多少。易知(下面p代表素数):

p h i ( p ) = p 1
素数与任意其他数互质。
p h i ( 2 x ) = p h i ( x )   ( x   )
p h i ( p k ) = ( p 1 ) p k 1
p a 不互质的个数有 p a 1 个,相减即是答案。
p h i ( m n ) = p h i ( m ) p h i ( n )   m n
因为欧拉函数是一个积性函数。
n = p 1 a 1 p 2 a 2 p k a k p h i ( n ) = n ( 1 1 p 1 ) ( 1 1 p 2 ) ( 1 1 p k )
根据下面第二个定理就可以得出来。
p h i ( n ) = p i | n ( p i 1 ) p i k i 1 = n p i | n ( 1 1 p i )
也可以容斥定理稍加分析就出来了。
d | n p h i ( d ) = n
莫比乌斯反演证明,不了解的可以看看这篇博客
https://blog.csdn.net/qq_41635132/article/details/82460942
x = b a p h i ( m ) 1   ( m o d   m ) a x = b   ( m o d   m )
证明很简单,可以参照费马小定理的证明,
https://blog.csdn.net/qq_41635132/article/details/82460062
小于 n 并且与 n 互质的数的和为 n p h i ( n ) 2 ,上次ccpc网络赛这道题目,被橘子大佬给秀了。
证明:如果 g c d ( n , i ) == 1     g c d ( n , n i ) == 1 反证法易证。
所以,与 n 互质的数都是成对出现,并且和为 n ,所以 a n s = n p h i ( n ) 2
i 时有
p h i ( i p ) = p h i ( i ) p

那么phi的筛法可以根据素数筛得来。
根据下面三条性质。
p h i ( p ) = p 1
p h i ( i p ) = p h i ( i ) p w h e n ( i   m o d   p == 0 )     w h e n ( g c d ( i , p ) ! = 1 )
p h i ( i p ) = p h i ( i ) ( p 1 )   w h e n ( i   m o d   p ! = 0 )

int prime[10000000],primesize,phi[10000000];
bool isprime[10000000];
void get_prime(int size){
    memset(isprime,true,sizeof isprime);
    isprime[0]=isprime[1]=false;
    phi[0]=0,phi[1]=1;
    for(int i=2;i<=size;i++){
        if(isprime[i]){
            prime[primesize++]=i;
            phi[i]=i-1;
        }
        for(int j=0;j<primesize&&i*prime[j]<=size;j++){
            isprime[i*prime[j]]=false;
            if(i%prime[j]==0){
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }
            phi[i*prime[j]]=phi[i]*(prime[j]-1);
        }
    }
}

根据第五个性质,可以 O ( n ) 的得到phi(n),适用于无法打表的情况。

ll Getphi(ll n){
    ll phi=n;
    for(ll i=2;i*i<=n;i++){
        if(n%i==0){
            phi/=i;
            phi*=i-1;
            while(n%i==0)n/=i;
        }
    }
    if(n!=1)phi/=n,phi*=n-1;
    return phi;
}

最后,来展示神级公式

a b   m o d   p = ( a   m o d   p ) p h i ( p ) + (   b   m o d   p h i ( p )   )   m o d   p


Miller-Rabin大素数测试

参照https://blog.csdn.net/ECNU_LZJ/article/details/72675595这个博客。
这里写图片描述
这里写图片描述
仔细看肯定能看懂,我代码里面会有我自己的解释,代码还是挺短,边理解边背也可以。

ll qmul(ll a,ll b,ll mod){//快速积代码
    ll res=0;
    while(b){
        if(b&1)res=(res+a)%mod;
        a=(a+a)%mod;
        b>>=1;
    }
    return res;
}
ll qpow(ll a,ll n,ll mod){//快速幂代码
    ll res=1;
    while(n){
        if(n&1)res=qmul(res,a,mod);
        a=qmul(a,a,mod);
        n>>=1;
    }
    return res;
}
bool Miller_Rabin(ll n){
    //初等判断
    if(n==2)return true;
    if(n<2||!(n&1))return false;
    //计算n-1=2^q*m
    ll m=n-1,k=0;
    while(!(m&1))k++,m>>=1;
    //Miller_Rabin测试次数
    for(int i=1;i<=20;i++){
        ll a=rand()%(n-1)+1;
        ll x=qpow(a,m,n);
        ll y;
        //对a^m,a^2m,a^4m……进行测试
        for(int j=1;j<=k;j++){
            y=qmul(x,x,n);
            if(y==1&&x!=1&&x!=n-1)return false;
            x=y;
        }
        //二次探测定理,不是1肯定为合数
        if(y!=1)return false;
    }
    return true;
}

但是这是对Miller-Rabin筛初步的理解,我们应该究其更深!

如果我们在上个世纪没有这个算法的时候会怎么想?
当然是粗略的筛一下!
比如我们知道除了2之外所有的偶数都是合数,这就是筛2之后的结果。
我们可以依次向后筛,等我们筛到13的时候,就剩下

( 1 1 2 ) ( 1 1 3 ) ( 1 1 5 ) ( 1 1 7 ) ( 1 1 11 ) ( 1 1 13 ) = 0.1918
已经有 4 5 的准确率了,并且这个函数递减有下界,肯定能筛完全。但是到后面性价比会变得越来越低。
那我们的工作就是寻找素性更强的筛法,于是乎欧拉托梦告诉我一个公式
a 2 1 ( m o d   p ) a ± 1 ( m o d   p )
证明详见我的另一篇博客。
https://blog.csdn.net/qq_41635132/article/details/82459692
那么我们现在有一个 p ,素性未知,我们当然可以先搞一搞这个 p 了。我们把 p 转化成 p 1 = 2 k t
然后我们任选一个 a ,如果 a t ± 1 ( m o d   p ) p 才有可能为素数。同理 a 2 t ± 1 ( m o d   p ) , a 4 t ± 1 ( m o d   p ) a 2 k t ± 1 ( m o d   p ) a p 1 ± 1 ( m o d   p )
是不是很熟悉?这就是费马小定理,有兴趣可以转的我的那一篇博客, https://blog.csdn.net/qq_41635132/article/details/82460062。现在思路清晰了吧?看看上面代码是不是就十分小儿科了。

杜教筛

水平不够,过段时间写。

猜你喜欢

转载自blog.csdn.net/qq_41635132/article/details/82423386