小黃書第19章p82頁根據合數的拉賓-米勒測試可得到素數的必要條件。
以POJ1811 Prime Test 爲例。
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
typedef long long LL;
const int S=20;
LL pfact[10005],ant;
LL multi_mod(LL a,LL b,LL c){ //防止a*b溢出!
LL ans=0;
while(b){
if(b&1){
ans=(ans+a)%c;
}
a<<=1;
if(a>=c)a%=c;
b>>=1;
}
return ans;
}
LL pow_mod(LL a,LL b,LL c){
LL ans=1;
while(b){
if(b&1){
ans=multi_mod(ans,a,c);
}
a=multi_mod(a,a,c);
b>>=1;
}
return ans;
}
bool check(LL a,LL n,LL x, LL t){
LL ret=pow_mod(a,x,n);
// LL last=ret;
if(ret==1||ret==n-1)return false;//根據合數的拉賓-米勒測試. 滿足a^x和1不同餘n的爲合數.
for(int i=1;i<=t;i++){
ret=multi_mod(ret,ret,n);
if(ret==n-1)return false;
//滿足對所有的i=0,1,2,...,t-1.a^(2^i*x)和-1不同餘n的爲合數.
// i=0時即使上面循環一開始的那個判定(ret==n-1)
// 所以在素數判定中,這兩個條件僅僅作爲必要條件而不是充分條件!
}
/* if(ret==1)return false;
else*/
return true;
}
bool Miller_Rabin(LL n){
if(n==2)return true;
if(n<2||n%2==0)return false;
LL x=n-1;
LL t=0;
while((x&1)==0){
x>>=1;
t++;
}
//上面的步驟求得n-1=2^k*q,q是奇數.
for(int i=0;i<S;i++){//隨機大法,S越大,成功率越大.也可以使用底數組合(2,7,61)。
LL a=rand()%(n-1)+1; //1-n
if(check(a,n,x,t))return false;//如果是合數,返回false;
}
return true;
}
LL gcd(LL a,LL b){
if(!b)return a;
else return gcd(b,a%b);
}
LL Pollard_rho(LL x, LL c){
//函數f(x)=(x^2+c)%n
LL i=1,k=2;
LL x0=rand()%x; // x1爲隨機數.
LL y=x0;
while(1){
x0=(multi_mod(x0,x0,x)+c)%x; //x0即x2,y即x1.
LL yx=y-x0;
if(yx<0)yx*=-1;
LL d=gcd(yx,x);// 求gcd的原因是存在更多的因數,而不是隻知道p整除n,n=p*q.當求gcd(x,n)>1時則有p,2p,3p,4p...
// 必須abs(y-x0).?
if(d>1&&d<=x)return d;
// if(d==x)return d,即if(y==x0)return x;
if(++i==k)y=x0,k<<=1;//Brents’Algorithm?效率更加?
}
}
void getpf(LL n){
if(Miller_Rabin(n)){
pfact[ant++]=n;
return ;
}
LL p=n;
while(p>=n){
p=Pollard_rho(p,rand()%(n-1)+1);
}
getpf(p);
getpf(n/p);
}
int main(){
int T;
LL n;
scanf("%d",&T);
while(T--){
scanf("%lld",&n);
if(Miller_Rabin(n)){
printf("Prime\n");
continue;
}
ant=0;
LL ans;
getpf(n);
ans=pfact[0];
for(int i=1;i<ant;i++)
ans=min(ans,pfact[i]);
printf("%lld\n",ans);
}
}