2018.12.19【BZOJ3667】【洛谷P4718】Rabin-Miller算法(Miller-Rabin)(Pollard-Rho)

DarkBZOJ传送门

洛谷传送门


解析:

M i l l e r − R a b i n Miller-Rabin MillerRabin模板解析
P o l l a r d − R h o Pollard-Rho PollardRho模板解析

之前写了半天的Pollard-Rho在洛谷上一直过不了,后来终于找到原因了,我真是够SB的

看一下Pollard-Rho的两种实现方式,(以下所有 l l ll ll均代指 l o n g l o n g long long longlong m u l ( a , b , c ) mul(a,b,c) mul(a,b,c)均表示在 % c \%c %c意义下的快速乘)
1.

inline ll Pollard_Rho(ll x){
     
    ll i=1,k=2,n=rand()%(x-1)+1,m=n,c=rand()%(x-1)+1;
    while(true){
     
        ++i;
        n=(mul(n,n,x)+c)%x;
        ll p=gcd((m-n+x)%x,x);
        if(p!=1&&p!=x)return p;
        if(m==n)return x;
        if(i==k)k<<=1,m=n;
    }
}
inline ll Pollard_Rho(ll x){
     
    ll n=0,m=0,t=1,q=1,c=rand()%(x-1)+1;
    for(ll k=2;;k<<=1,m=n,q=1){
     
    	for(ll i=1;i<=k;++i){
     
    		n=(mul(n,n,x)+c)%x;
    		q=mul(q,abs(m-n),x);
        }
        t=gcd(x,q);if(t>1)return t;
    }
}

稍有常识的人就 能看出第一份是要明显慢于第二份的。因为关键操作 g c d gcd gcd次数的不同。

第一份在每次循环中都需要计算 g c d gcd gcd,而第二份只会在每次完成倍增后计算 g c d gcd gcd。期望计算次数第二份小于第一份。

感觉是期望 O ( n 1 2 ) O(n^{\frac{1}{2}}) O(n21)和期望 O ( n 1 4 ) O(n^{\frac{1}{4}}) O(n41)次计算 g c d gcd gcd的差别。

如果有计算出精确计算规模差距的读者,希望能够告知博主。


代码:

#include
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define pc put_char
#define puts put_s
#define cs const

namespace IO{
     
    cs int Rlen=1<<18|1;
    char buf[Rlen],*p1=buf,*p2=buf;
    char obuf[Rlen],*p3=obuf;
    
    inline char get_char(){
     
        return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
    }
    
    inline ll getint(){
     
        re ll num;
        re char c;
        while(!isdigit(c=gc()));num=c^48;
        while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
        return num;
    }
    
    inline void put_char(char c){
     
        *p3++=c;
        if(p3==obuf+Rlen)fwrite(obuf,1,Rlen,stdout),p3=obuf;
    }
    
    inline void put_s(cs char *s){
     
        for(int re i=0;s[i];++i)pc(s[i]);pc('\n');
    }
    
    inline void outint(ll a){
     
        static char ch[23];
        if(a==0)pc('0');
        while(a)ch[++ch[0]]=a-a/10*10,a/=10;
        while(ch[0])pc(ch[ch[0]--]^48);pc('\n');
    }
    
    inline void FLUSH(){
     
        if(p3==obuf)return ;
        fwrite(obuf,1,p3-obuf,stdout);
        p3=obuf;
    }
}
using namespace IO;

inline ll mul(ll a,ll b,ll mod){
     
    return (a*b-(ll)((long double)a/mod*b)*mod+mod)%mod;
}

inline ll quickpow(ll a,ll b,ll mod){
     
    re ll ans=1;
    for(;b;b>>=1,a=mul(a,a,mod))if(b&1)ans=mul(ans,a,mod);
    return ans;
}

cs int P=1007;
bitset<P> mark;
int prime[P],pcnt;
int maxpri[P];
inline void linear_sieves(int len=P-7){
     
    mark[1]=true;
    for(int re i=2;i<=len;++i){
     
        if(!mark[i])prime[++pcnt]=i,maxpri[i]=i;
        for(int re j=1;j<=pcnt&&i*prime[j]<=len;++j){
     
            mark[i*prime[j]]=true;
            maxpri[i*prime[j]]=maxpri[i];
            if(i==i/prime[j]*prime[j])break;
        }
    }
}

inline bool isprime(ll x){
     
    if(x<=P-7)return !mark[x];
    if(!(x&1)||(x%3==0)||(x%5==0)||(x%7)==0||(x%61==0)||(x%24251)==0)return false;
    ll t=x-1,s=0;
    while(!(t&1))t>>=1,++s;
    for(int re i=1;i<=5;++i){
     
    	ll p=prime[rand()%pcnt+1]%x;
        ll num=quickpow(p,t,x),pre=num;
        if(x%p==0)return false;
        for(int re j=0;j<s;++j){
     
            num=mul(num,num,x);
            if(num==1&&pre!=1&&pre!=x-1)return false;
            pre=num;
        }
        if(num!=1)return false;
    }
    return true;
}

inline ll gcd(ll a,ll b){
     
    if(!a||!b)return a+b;
    re int t=__builtin_ctzll(a|b);
    a>>=__builtin_ctzll(a);
    while(b){
     
        b>>=__builtin_ctzll(b);
        if(a>b)a^=b,b^=a,a^=b;
        b-=a;
    }
    return a<<t;
}

ll ans;
inline ll Pollard_Rho(ll x){
     
    if(x%2==0)return 2;
    if(x%3==0)return 3;
    if(x%5==0)return 5;
    if(x%7==0)return 7;
    if(x%61==0)return 61;
    if(x%24251==0)return 24251;
    ll n=0,m=0,t=1,q=1,c=rand()%(x-1)+1;
    for(ll k=2;;k<<=1,m=n,q=1){
     
    	for(ll i=1;i<=k;++i){
     
    		n=(mul(n,n,x)+c)%x;
    		q=mul(q,abs(m-n),x);
        }
        t=gcd(x,q);if(t>1)return t;
    }
}

inline void sieves(ll x){
     
    if(x==1)return ;if(x<=ans)return ;
    if(isprime(x))return (void)(ans=max(ans,x));
    if(x<=P-7)return (void)(ans=max(ans,(ll)maxpri[x]));
    re ll p=x;
    while(p>=x)p=Pollard_Rho(p);
    sieves(p);
    while(x%p==0)x/=p;
    sieves(x);
}

int T;
signed main(){
     
    srand(time(0));
    linear_sieves();
    T=getint();
    while(T--){
     
        ll n=getint();
        if(n<=P-7)mark[n]?outint(maxpri[n]):puts("Prime");
        else {
     
            ans=0;
            sieves(n);
            if(ans==n)puts("Prime");
            else outint(ans);
        }
    }
    FLUSH();
    return 0;
}

你可能感兴趣的:(素数测试,分解质因数)