Miller Rabin与Pollard Rho
先写一下Miller Rabin,主要用于判断一个比较大的数是否是质数
解释一个东西。PPT里面在举例子的时候,用
费马小定理的作用是什么?其实就是提供了多个类似于二次探测定理的等式(不然在哪里去找满足二次探测定理的等式?),然后利用模的运算法则进行二次探测定理的测试
接下来的操作步骤,当结果是1的时候,我们继续就更能增加
实现一般都采取倒序(见以下的code),应该是可以避免除法带来的逆元运算(倒序的时候为什么某次结果为
bool MillerRabin(ll x)
{
if((x&1)==0) return x==2;
ll u=x-1;
int t=0;
while((u&1)==0) u>>=1,t++;
for(int i=1;i<=8;i++)
{
ll a=rand()%(x-2)+2;
ll v=quickpow(a,u,x);
if(v==1) continue;
bool flag=0;
for(int j=0;j<t;j++)//想一想为什么是小于而不是小于等于
{
if(v==x-1) break;
//注意这里我们是倒序实现的
//如果某一次既不为1也不为n-1
//那么一定不能直接返回0
//因为正常的算法过程是正序的
//如果按照正常的算法过程
//某一次结果为n-1
//那么继续计算的话
//结果是不一定为1或者n-1的
//所以一定要一直计算下去
//否则会错误
v=(__int128)v*v%x;
if(j==t-1) flag=1;
}
if(flag) return 0;
}
return 1;
}
注意Miller—Rabin中间可能要用快速乘法
对于Pollard Rho算法(主要用于将
首先了解生日悖论
那么生日悖论在此算法中的作用就是告诉我们,如果在[1,n]中随机生成数,期望在至多
那么在此算法中,我们随机生成[0,n-1]中的数(以下讨论的n都是合数,对于质数可以直接通过Miller-Rabin判断),假设n的一个
那么用于生成这个随机数列的函数众所周知,也带来了循环问题(想想随机数函数的内部实现,模运算有循环节)。那么判断环就是用Floyd判圈法。为什么要用这个方法?因为这个方法可以节省空间(可以设想一下,如果普通的判圈,是不是要把出现的数字全部记录下来,这样是很费空间的,甚至会MLE)
那么此时复杂度就已经到达了
ll Pollard_Rho(ll N)
{
if (N == 4) // 特判4
return 2;
if (is_prime(N)) // 特判质数
return N;
while (1)
{
ll c = randint(1, N - 1); // 生成随机的c
auto f = [=](ll x) { return ((lll)x * x + c) % N; }; // lll表示__int128,防溢出
ll t = f(0), r = f(f(0));
while (t != r)
{
ll d = gcd(abs(t - r), N);
if (d > 1)
return d;
t = f(t), r = f(f(r));
}
}
}
那么消除log就可以利用累乘的方法。乘法累计的操作就是判断在数列迭代的过程中,是否会存在一个gcd是大于1的,如果存在那么累乘结果的gcd也会大于1,如果不存在那么累乘结果的gcd也是1,这样就可以节省更多的时间。为什么选择128次为一个周期?不太清楚,但是很优秀的选择
ll Pollardrho(ll x)
{
if(x==4) return 2;
while(1)
{
ll c=rand()%(x-1)+1;
ll t=0,r=0,p=1,q;
do{
for(int i=1;i<=128;i++)
{
t=f(t,c,x),r=f(f(r,c,x),c,x);
q=(__int128)p*abs(t-r)%x;
if(t==r||!q) break;//如果遇到环了或者累乘结果为0
//就可以退出循环了
//这样无非是多增加一些判断时间而已
p=q;
}
ll d=gcd(p,x);
if(d>1) return d;
}while(t!=r);
}
}
然后也可以使用倍增优化。上面这个代码有一个问题,就是如果累乘中途的某个结果去算gcd不为0且大于1,但是最终累乘完毕之后累乘结果为0了,就会跳过这次判断,无疑会增加时间复杂度,所以我们循序渐进,按照1次,2次,4次,8次……128次去判断,这样就可以解决这个问题了
ll Pollardrho(ll x)
{
if(x==4) return 2;
while(1)
{
ll c=(ll)rand()%(x-1)+1;
ll t=0,r=0,p=1,q;
int step,goal=1;
bool flag=0;
for(;;goal<<=1)
{
for(step=1;step<=goal;step++)
{
t=f(t,c,x),r=f(f(r,c,x),c,x);
q=(__int128)p*abs(t-r)%x;
if(t==r||!q)
{
flag=1;
break;
}
p=q;
if(step%127==0){
ll d=gcd(p,x);
if(d>1) return d;
}
}
ll d=gcd(p,x);
if(d>1) return d;
if(flag) break;
}
}
}
那么找到一个因子之后,如果找最大质因子?见如下代码,类似于分治
void maxprimefactor(ll x)
{
if(x==1||x<=maxfactor) return;
if(MillerRabin(x)) maxfactor=max(maxfactor,x);
else {
ll p=Pollardrho(x);
while(x%p==0) x/=p;//小优化
maxprimefactor(p);
maxprimefactor(x);
}
}
对于完整的代码,可以去看看洛谷P4718,比较一下两份AC代码的区别和时间
尝试用生日悖论做一下这道题目
前置结论:斐波那契数列的循环节长度不会超过
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
2021-09-03 墨墨的等式【同余最短路模板题】