被这道题虐了两天了……自己也试着敲了一下。。事实证明手上的模版不给力啊。。。先用手上的模版敲了一个miller rabin,结果9都给我判成素数,真是无言了,然后又用算导上面讲得方法自己敲了一个pollard rho,结果卡死了……只有少数几种情况会出答案,大部分情况都是卡死了。。悲剧一个。。后来去找解体报告,因为这个题本来就是模版题嘛。。大多数写的都不是很符合我的习惯,看不进去,好不容易找到两个我看得进去的吧,自己在hust上交了一下,居然RE了。。话说这种解题报告还交上来嘛。。。好不容易在百度知道上面找到了一份代码,我觉得写得挺不错的,也解决了一些我遇到的困惑。
素数判断用miller法 分解用pollard法 关键有几点
1:用2分法作64位乘法必须用unsigned __Int64 否则位移的时候会带符号(符号位移不掉)
2:pollard会陷入死循环 所以要加卡时 如果超过多少次还没出来就return 1,换个初始数继续
3:所有<<号,>>号必须全部加括号 像b1=(x<<32)>>32这种等号后面没加括号的是错误的 应该是b1=((x<<32)>>32);
4:发现pollard算法中用x*x-1产生随机数,如果那个-1改成其他数 效率会不一样 根据frkstyc大牛的代码 x*x+16381要将近快一倍(可是这位仁兄用的是10007。。。我自己换别的数字试了一下,确实没有更快的)
a27400 | 1811 | Accepted | 412K | 375MS | G++ | 1872B | 2011-09-02 17:07:07 |
//写得太好了,每一个函数都是一个模版呀~~~~
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define gcc 10007
#define MAX ((INT)1<<63)-1
using namespace std;
typedef unsigned long long INT;
INT p[10]={2,3,5,7,11,13,17,19,23,29};
inline INT gcd(INT a,INT b)
{
INT m=1;
if(!b) return a;
while(m)
{
m=a%b;
a=b;
b=m;
}
return a;
}
//计算a*b%n
inline INT multi_mod(INT a,INT b,INT mod)
{
INT sum=0;
while(b)
{
if(b&1) sum=(sum+a)%mod;
a=(a+a)%mod;
b>>=1;
}
return sum;
}
//计算a^b%n;
inline INT quickmod(INT a,INT b,INT mod)
{
INT sum=1;
while(b)
{
if(b&1) sum=multi_mod(sum,a,mod);
a=multi_mod(a,a,mod);
b>>=1;
}
return sum;
}
bool miller_rabin(INT n)
{
int i,j,k=0;
INT u,m,buf;
//将n分解为m*2^k
if(n==2)
return true;
if(n<2||!(n&1))
return false;
m=n-1;
while(!(m&1))
k++,m>>=1;
for(i=0;i<9;i++)
{
if(p[i]>=n)
return true;
u=quickmod(p[i],m,n);
if(u==1)
continue;
for(j=0;j<k;j++)
{
buf=multi_mod(u,u,n);
if(buf==1&&u!=1&&u!=n-1)
return false;
u=buf;
}
//如果p[i]^(n-1)%n!=1那么n为合数
if(u-1)
return false;
}
return true;
}
//寻找n的一个因子,该因子并不一定是最小的,所以下面要二分查找最小的那个因子
INT pollard(INT n)
{
INT i=1;
INT x=rand()%(n-1)+1;
INT y=x;
INT k=2;
INT d;
do
{
i++;
d=gcd(n+y-x,n);
if(d>1&&d<n)
return d;
if(i==k)
y=x,k*=2;
x=(multi_mod(x,x,n)+n-gcc)%n;
}while(y!=x);
return n;
}
INT MIN;
INT pollard_min(INT n)
{
INT p,a,b=MAX;
if(n==1) return MAX;
if(miller_rabin(n)) return n;
p=pollard(n);
a=pollard_min(p);//二分查找
INT y=n/p;
b=pollard_min(y);
return a<b?a:b;
}
int main(void)
{
INT T;
INT n;
scanf("%lld",&T);
while(T--)
{
scanf("%lld",&n);
MIN=MAX;
if(miller_rabin(n))
{
puts("Prime");
}
else printf("%lld\n",pollard_min(n));
}
return 0;
}