学习笔记:
神仙算法,学了我两天,好多地方的博客虽然内容没有讲错,但是逻辑却有一点小问题,误导了我好久。。。
Miller_Rabin:
- 首先我们要会判断质数,最古老的枚举显然在这里不够用,所以我们要用到,费马小定理,即 ap−1≡1mod p a p − 1 ≡ 1 mod p ,同时还要求 a,p a , p 互质。
- 但是这还不够,因为有不是素数的数也满足这个条件,所以我们还要用一次二次探测定理,即若 p p 为素数且则有 a≡1modp a ≡ 1 mod p 或 a≡−1modp a ≡ − 1 mod p ,证明的话把 1 1 移到左边去平方差一下然后用唯一分解定理即可。
- 但是这样还会极小的例外,这时为了避免这种人品上的问题,别忘了最开始筛一下,因为这样过后绝大部分的自然数就已经筛没了。
Pollard_Rho:
- 先来考虑一个比较运气的东西,就是用随机数来试着看能不能整除。
- 显然上面的做法效率太低,我们来看一下生日悖论给我们的启发:
生日悖论,指如果一个房间里有23个或23个以上的人,那么至少有两个人的生日相同的概率要大于50%。这就意味着在一个典型的标准小学班级(30人)中,存在两人生日相同的可能性更高。对于60或者更多的人,这种概率要大于99%。
所以上面到底讲了什么呢?总结来说就是在一定的范围内随机选择一些数,当选的数的个数 ≥n−−√ ≥ n (n是范围的大小)时,有相当大的概率会出现相同的数。考虑怎么将这个神奇的发现运用到分解约数上去,每一个随机数在模因数 p p 下可以看做是一个小于的数(虽然这个 p p 我们不知道是多少,但是它仍旧在那里),两个模相同的数作差的话肯定就是 p p 的倍数了,所以我们的目标现在就变成了找到两个模意义下相同的数来做差,然后用 gcd gcd 来判断是否成立。这样根据生日悖论,我们只要用大量的随机数来两两相减就一定能试出一个约数。这就是 PollardRho P o l l a r d R h o 算法的理论基础。
但是说了这么多
PollardRho
P
o
l
l
a
r
d
R
h
o
的这个
ρ
ρ
还没有体现出来,同时我们发现如果两两作差的话,虽然随机数的量
≤n14
≤
n
1
4
,但是运算量还是高达
n−−√
n
,在信息学竞赛这个
1e18
1
e
18
的范围下显然有问题,这个时候就要敢于做梦,想象一下如果我们在把这
n14
n
1
4
个随机数造出来了之后就可以找到相同的数那该有多好,那么就要用到一个函数
f(x)=(x2+c)modn
f
(
x
)
=
(
x
2
+
c
)
mod
n
(这个
n
n
是待分解的数)我们可以用这个函数不断地制造伪随机数,观察一下这个函数的特性,即后一个随机数只和前一个随机数有关,再进一步地推导,根据整数同余的相关性质又可以得出来后一个数模的值只和前一个数模
p
p
的值有关,虽然我们不知道是多少,但是可以确定的是
n
n
是的倍数,所以在这样不断制造随机数的过程中,在模
n
n
意义下还没有形成环的时候,模意义下早已形成了环,并且环的大小在期望意义下
≤p–√
≤
p
,这时只要在模
n
n
意义下一个一个地试就好了,至于判断模意义下的环并且跳出,网上的资料太多,有Floyd和Brent两种,这里就不再赘述。
注意溢出和常数的影响。
/*============================================
* Author : ylsoi
* Problem : Pollard Rho
* Algorithm : Pollard Rho and Miller Rabin
* Time : 2018.7.15
* ==========================================*/
#include<bits/stdc++.h>
#define REP(i,a,b) for(register int i=a;i<=b;++i)
typedef long long ll;
using namespace std;
void File(){
freopen("Pollard_Rho.in","r",stdin);
freopen("Pollard_Rho.out","w",stdout);
}
ll qmul(ll x,ll b,ll mod){
ll tmp=(x*b-(ll)((long double)x/mod*b+1e-8)*mod);
return tmp<0?tmp+mod:tmp;
/*ll ret=0,base=x%mod;
while(b){
if(b&1)ret=(ret+base)%mod;
base=(base+base)%mod;
b>>=1;
}
return ret;*/
}
ll qpow(ll x,ll b,ll mod){
ll ret=1,base=x%mod;
while(b){
if(b&1)ret=qmul(ret,base,mod);
base=qmul(base,base,mod);
b>>=1;
}
return ret;
}
bool Miller_Rabin(ll p){
if(p==1 || p==2 || p==3 || p==5 || p==7 || p==11 || p==13)return true;
if(p%2==0 || p%3==0 || p%5==0 || p%7==0 || p%11==0 || p%13==0)return false;
ll t=p-1,cnt=0;
while(t%2==0)t>>=1,++cnt;
REP(i,1,10){
ll a=rand()%(p-1)+1,x=qpow(a,t,p);
REP(j,1,cnt){
ll y=qmul(x,x,p);
if(y==1 && x!=1 && x!=p-1)return false;
x=y;
}
if(x!=1)return false;
}
return true;
}
ll gcd(ll x,ll y){return x%y==0 ? y : gcd(y,x%y);}
ll Pollard_Rho(ll x){
ll c=rand()%(x-1)+1,a=rand()%(x-1)+1,b=a,g;
for(int k=1,step=1;;++step){
b=(qmul(b,b,x)+c)%x;
g=gcd(abs(a-b),x);
if(g!=1 && g!=x)return g;
if(a==b)return x;
if(k==step){
a=b;
k<<=1;
}
}
/*ll c=rand(),a=rand(),b=(qmul(a,a,x)+c)%x,g;
while(1){
g=gcd(abs(a-b),x);
if(g!=1 && g!=x)return g;
a=(qmul(a,a,x)+c)%x;
b=(qmul(b,b,x)+c)%x;
b=(qmul(b,b,x)+c)%x;
if(a==b)return x;
}*/
}
int n;
ll ans;
void findfac(ll x){
if(Miller_Rabin(x)){
ans=max(x,ans);
return;
}
ll g=Pollard_Rho(x);
while(g==x)g=Pollard_Rho(x);
findfac(x/g);
findfac(g);
}
int main(){
File();
srand((unsigned)time(NULL));
scanf("%d",&n);
REP(i,1,n){
ll x;
scanf("%lld",&x);
if(Miller_Rabin(x))puts("Prime");
else{
ans=0;
findfac(x);
printf("%lld\n",ans);
}
}
//cerr<<clock()<<endl;
return 0;
}