米勒来宾素数测试与Pollard rho算法
mod998244353 · · 个人记录
可能更好的阅读体验
米勒来宾素数测试
此处只考虑如何判断奇素数
根据费马小定理:
当
就可以写一个用快速幂判断一个数是不是奇素数
#include<bits/stdc++.h>
using namespace std;
const int plist[]= {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97};
//素数表
const int L=25;
int fastpow(int a,int k,int mod) {
int base=1;
while(k) {
if(k&1) base=base*1ll*a%mod;
a=a*1ll*a%mod;
k>>=1;
}
return base;
}
bool isprime(int p) {
if(p<=100) {//在表内就直接判断
for(int i=0; i<L; ++i) {
if(plist[i]==p)return true;
}
return false;
}
for(int i=0; i<L; ++i) {//否则看看是否符合费马小定理
if(fastpow(plist[i],p-1,p)!=1)return false;
}
return true;
}
int n;
int main() {
scanf("%d",&n);
printf(isprime(n)?"YES":"NO");
return 0;
}
但这个效率低,而且错误的概率很大。
我们可以发现,当
当
否则当
所以我们可以用
同时计算
const int plist[]= {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97};
//素数表
const int L=25;
inline ll mul(const ll&a,const ll&b,const ll&mod) { //可能爆ll的计算a*b%mod
ll r=((long double)a*b/mod+0.5); //注:用这个不要用太大的模数
r=a*b-r*mod;
return r<0?r+mod:r;
}//具体为什么可以这样,搜索“骆可强 快速乘”
inline ll fastpow(ll a,ll k,const ll&mod) { //快速幂
ll ans=1;
while(k) {
if(k&1)
ans=mul(ans,a,mod);
a=mul(a,a,mod);
k>>=1;
}
return ans;
}
bool mr(const ll&n,const ll&a) { //用a来hack掉n,是合数就返回true
ll t=n-1;
const ll p=t;
while(!(t&1))t>>=1; //除到t为奇数
ll buf=fastpow(a,t,n);
if(buf==1||buf==p)return 0; //通过测试
while((t<<=1)<p) { //和上面1到r-1是一样的
buf=mul(buf,buf,n);
if (buf==p)return 0; //通过测试
}
return 1;
}
bool ptest(ll n) {
if(n<2ll)
return 0;
if(!(n&1))return n==2; //偶数的情况
for(int i=0; i<L; ++i) {//小范围的判断整除
if(n==plist[i])return true;
if(!(n%plist[i]))return false;
}
return n<=10000ll?1:!mr(n,2)&&!mr(n,61)&&!mr(n,233);//用这三个素数就够了
}
时间复杂度
Pollard rho质因数分解
P4718 【模板】Pollard-Rho算法
小结论1(生日悖论)
随便找
证明:
不相同的概率:
相同的概率即是
扩展:
随机选出
算法思路
先通过一个方法找到一个因子
现在要考虑怎么找一个因子。
假设合数
也就是说,我们可以用
问题便转换成求一个
考虑一个数列
其中
这个函数还算随机(即算个伪随机数),所以根据上面的小结论,大约到
每个
第一张是模
第二张是模
可以发现
所以当且仅当
这个情况特别小,因为
接下来就是要考虑判断环。
追及法求环
我们可以用两个变量
也就是看
那么如果
如果
否则如果
根据小结论,这个是期望
若失败,则重置
小优化
由于
也就是说,时间复杂度为
我们发现当
我们可以一次存下
大于
否则重新再找
一般
时间复杂度
#include <bits/stdc++.h>
using namespace std;//以下大段为米勒来宾素数测试
const int plist[]= {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97};
const int L=25;
typedef long long ll;
inline ll read() {
bool f=1;
ll x=0;
int c=getchar();
while(c<=47||c>=58)f&=c!=45,c=getchar();
while(c>=48&&c<=57) {
x=(x<<3)+(x<<1)+(c&15);
c=getchar();
}
return f?x:-x;
}
inline ll mul(const ll&a,const ll&b,const ll&mod) {
ll r=((long double)a*b/mod+0.5);
r=a*b-r*mod;
return r<0?r+mod:r;
}
inline ll fastpow(ll a,ll k,const ll&mod) {
ll ans=1;
while(k) {
if(k&1)
ans=mul(ans,a,mod);
a=mul(a,a,mod);
k>>=1;
}
return ans;
}
bool mr(const ll&n,const ll&a) {
ll t=n-1;
const ll p=t;
while(!(t&1))t>>=1;
ll buf=fastpow(a,t,n);
if(buf==1||buf==p)return 0;
while((t<<=1)<p) {
buf=mul(buf,buf,n);
if (buf==p)return 0;
}
return 1;
}
bool ptest(ll n) {
if(n<2ll)
return 0;
if(!(n&1))return n==2;
for(int i=0; i<L; ++i) {
if(n==plist[i])return true;
if(!(n%plist[i]))return false;
}
return n<=10000ll?1:!mr(n,2)&&!mr(n,61)&&!mr(n,233);
}//上面都是米勒来宾素数测试
inline ll gcd(ll a,ll b) {//gcd
a=abs(a),b=abs(b);
if(!a)return b;
for(; b; swap(a,b))
a%=b;
return a;
}
const int lim=128;//z
ll sav[150];//存z个值的数组
int tot;
ll prho(const ll&n,const ll&c) {//追及法求环
ll x1=c+1,x2=mul(x1,x1,n)+c,buf=1;//先取第一个fi
tot=0;
if(x1>=n)x1-=n;
if(x2>=n)x2-=n;
while(1) {
buf=mul(x1-x2,buf,n);//乘上要测试的数的值
++tot;
sav[tot]=x1-x2;//存下要测试的数
if(tot==lim) {//够z个数了
if(gcd(buf,n)>1)break;//满足要求
tot=0;//重新开始存
}
x1=mul(x1,x1,n)+c;
x2=mul(x2,x2,n)+c;
x2=mul(x2,x2,n)+c;//往后跳
}
for(int i=1; i<=tot; ++i) {
buf=gcd(sav[i],n);
if(buf>1)return buf;//返回值
}
return n;//不成功
}
void solve(const ll&n,ll&ans) {
if(n<=ans)return ;//剪枝
if(ptest(n)) {//是素数
ans=max(ans,n);
return ;
}
const ll p=n-1;
ll sav=prho(n,rand()%p+1);
while(sav==n)sav=prho(n,rand()%p+1);//没成功就换一个c值
solve(sav,ans);//递归
solve(n/sav,ans);
}
ll pollard_rho(ll n) {
ll ans=0;
if(!(n&1)) {
ans=2;
while(!(n&1))n>>=1;
}
const int p=sqrt(min(10000ll,n));
for(int i=3; i<=p; i+=2)
if(!(n%i)) {
ans=i;
n/=i;
while(!(n%i))n/=i;
}//上面是先把小范围的素数判了
solve(n,ans);
return ans;
}
int main() {
srand(233);
for(int T=read(); T; --T) {
ll n=read();
ll ans=pollard_rho(n);
if(ans==n)printf("Prime\n");
else printf("%lld\n",ans);
}
return 0;
}