Mr素性测试+Prho分解小记
command_block · · 个人记录
一些玄学的东西……
Mr素性测试·前置芝士
(下面的
费马小定理:
那么反过来,
二次探测定理:
首先,在模
m 意义下,所有数的m-1 次方形成一个乘法群Z_*^m 。封闭性 :a^{m-1}b^{m-1}=(ab)^{m-1} 那么,所有不好的数的
m-1 次方也形成一个乘法群。封闭性 :a^{m-1}b^{m-1}=1 ,仍然在群里。那么,如果我们存在一个好的数,就说明不是所有数都是坏的,那么不好的群就是
Z_*^m 的真子群,其大小是|Z_*^m| 的约数(除去其本身)那么坏的数至多占
|Z_*^m| 这个群的\frac{1}{2} 。
这就是Fermat素性测试。
问题是,存在一些(较多)神奇的合数,能通过任意的
为了保证正确率,我们考虑利用二次探测定理:
令
首先计算hack失败。
否则,这只可能是
这玩意利用二次剩余理论证明一下也可以得到每次测试概率大概是
但是,把两个定理相配合,相当于把两个反例集合交集,基本变成了空集……
这样子错误率就大大减小了,对于一个合数,可以证明一次测试成功hack的概率约为
那么试个
实战中,选择
Code:
并没有使用快速乘。
#include<algorithm>
#include<cstdio>
#define ll long long
using namespace std;
ll powM(ll a,ll t,ll m)
{
ll ans=1;
while(t){
if (t&1)ans=ans*a%m;
a=a*a%m;
t>>=1;
}return ans;
}
bool mr(ll n,ll a)
{
ll t=n-1;
while(!(t&1))t>>=1;
ll buf=powM(a,t,n);
if (buf==1||buf==n-1)return 0;
while((t<<=1)<n-1){
buf=buf*buf%n;
if (buf==n-1)return 0;
}return 1;
}
const int testp[8]={2,3,5,7,13,19,61,233};
bool ptest(ll n)
{
if (n<2)return 0;
for (int i=2;i*i<=min(n,10000ll);i++)
if (n%i==0)return 0;
if (n<=10000)return 1;
for (int i=0;i<8;i++)
if (mr(n,testp[i]))return 0;
return 1;
}
int n;
int main()
{
scanf("%d%d",&n,&n);
for (int i=1,a;i<=n;i++){
scanf("%d",&a);
if (ptest(a))puts("Yes");
else puts("No");
}return 0;
}
Prho分解·问题引入
P4718 【模板】Pollard-Rho算法
大概就是给你一个long long范围内的数,要求你分解。
暴力试除法
Pro分解效果 :
Prho分解·前置芝士
Mr素性测试(两者关系并不十分紧密)
生日悖论(怪论):
随便找23个人,他们之中有任意两人生日相同的概率已经大于
也就是 : 随机写
Prho分解·干货
我们先考虑通过某种方法得到
拿Mr特判掉
我们假设
每步都要求gcd代价有点高,这也导致复杂度多个
注意到,我们求出的
又注意到:
我们可以把连续
假如大于1,那么这一段内就找到了解,回头一个一个跳就好了。
取
通过上述算法,我们就能得到
然后我们继续分治分解
通常先暴力试除一些素数来减小常数,避开边界。
Code:
追及法:
#include<algorithm>
#include<cstdio>
#define ll long long
using namespace std;
inline ll mul(ll a,ll b,ll m){
ll r=((long double)a/m*b+0.5);
r=a*b-r*m;
return r<0?r+m:r;
}
ll powM(ll a,ll t,ll m)
{
ll ans=1;
while(t){
if (t&1)ans=mul(ans,a,m);
a=mul(a,a,m);
t>>=1;
}return ans;
}
bool mr(ll n,ll a)
{
ll t=n-1;
while(!(t&1))t>>=1;
ll buf=powM(a,t,n);
if (buf==1||buf==n-1)return 0;
while((t<<=1)<n-1){
buf=mul(buf,buf,n);
if (buf==n-1)return 0;
}return 1;
}
const int testp[8]={2,3,5,7,13,19,61,233};
bool ptest(ll n)
{
if (n<2)return 0;
for (int i=2;i*i<=min(n,10000ll);i++)
if (n%i==0)return 0;
if (n<=10000)return 1;
for (int i=0;i<8;i++)
if (mr(n,testp[i]))return 0;
return 1;
}
inline ll gcd(ll a,ll b){
if(a==0)return b;
if(a<0)a=-a;
ll t;
while(b){t=a%b;a=b;b=t;}
return a;
}
ll sav[150];int tot;
const int lim=128;
ll prho(ll n,ll c)
{
ll x1=(c+1)%n,x2=(mul(x1,x1,n)+c)%n,buf;
tot=0;
while(1){
buf=mul(x1-x2,buf,n);
sav[++tot]=x1-x2;
if (tot==lim){
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;
}
long long ans;
void solve(ll n)
{
if (n<=ans)return ;
if (ptest(n)){ans=max(ans,n);return ;}
ll sav=prho(n,rand()%(n-1)+1);
while(sav==n)sav=prho(n,rand()%(n-1)+1);
solve(sav);solve(n/sav);
}
int main()
{
srand(233);
int T;scanf("%d",&T);
for (int qt=1;qt<=T;qt++){
ll a,sav;
scanf("%lld",&a);
if (ptest(a)){
puts("Prime");
continue;
}sav=a;ans=0;
for (int i=2;i*i<min(10000ll,a);i++)
if (a%i==0){
ans=i;
while(a%i==0)a/=i;
}
solve(a);
printf("%lld\n",ans);
}return 0;
}
倍增法:
ll prho(ll n,ll c)
{
tot=0;
ll x,y=0,mt=1;
for (int st=1;;st<<=1){
x=y;
for (int i=0;i<st;i++){
y=mul(y,y,n)+c;
mt=mul(y-x,mt,n);
sav[++tot]=y-x;
if (tot==lim){
if (gcd(mt,n)>1)break;
tot=0;
}
}if (tot==lim)break;
}
for (int i=1;i<=tot;i++){
mt=gcd(sav[i],n);
if (mt>1)return mt;
}return n;
}