二次剩余 Cipolla 算法浅析
参考资料
求解
仅介绍模数 p 为奇素数的解法,也就是 Cipolla 算法。
判定是否存在二次剩余
设 ,由于原根环的长度为 (是个偶数),
列出方程 ,根据贝祖定理,当且仅当 时有解。
因此
- 在 为偶数时有解 和 。
- 在 为奇数时无解。
用原根求解较慢,尝试寻找等价条件。
- 在 为偶数时,
- 在 为奇数时,,由于 ,因此 。但是 ,所以 。
因此 有无解等价于 是否为 。
上述证明过程的一些推论
- 若有解,必有两个 ,因为 和 的奇偶性不同。同时不会有更多个,因为 的步长为 加两次又回到第一个点了。
- 在 意义下有 个二次剩余, 个二次非剩余。(考虑 是偶数, 的数中有几个偶数和奇数)。
二次剩余的求解
先随机出一个 ,使 为二次非剩余。
扩域,假设存在一个 使得 。
则 。
- 引理1:(证明:)
- 引理2: 证明:.
有
因此 为一个解。
容易得出另一个解为
Code
copy#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<iostream>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> PLL;
LL power(LL x,LL k,LL MOD)
{
LL res=1; x=x%MOD;
while(k) {
if(k&1) res=res*x%MOD;
x=x*x%MOD; k>>=1;
}
return res%MOD;
}
PLL mul(PLL a,PLL b,LL IOI,LL p)
{
return PLL((a.first*b.first%p+a.second*b.second%p*IOI%p)%p,(a.second*b.first%p+a.first*b.second%p)%p);
}
bool residue(LL n,LL p,LL& x0,LL& x1)
{
if(n==0) return (x0=x1=0,true); // 注意这个特判
if(power(n,(p-1)>>1,p)!=1) return false;
LL a=rand()%p;
while(!a || power(a*a-n+p,(p-1)>>1,p)==1)
a=rand()%p;
LL IOI=(a*a-n+p)%p;
LL k=(p+1)>>1;
PLL res(1,0),x(a,1);
while(k) {
if(k&1) res=mul(res,x,IOI,p);
x=mul(x,x,IOI,p); k>>=1;
}
x0=res.first; x1=p-x0;
if(x0>x1) swap(x0,x1);
return true;
}
int T;
LL n,p;
int main()
{
srand(time(0));
cin>>T;
while(T--) {
cin>>n>>p;
LL x0,x1;
if(!residue(n,p,x0,x1)) puts("Hola!");
else printf("%lld %lld\n",x0,x1);
}
return 0;
}
应用
本身可以解一元二次方程
也可以配合 exgcd,BSGS,原根之类解更多方程。
求解 一元二次方程 在模意义下的根。
copybool solve(LL a,LL b,LL c,LL p,LL& x0,LL& x1)
{
a=(a%p+p)%p; b=(b%p+p)%p; c=(c%p+p)%p;
LL d=(b*b-4*a*c%P+P)%P;
if(!residue(d,p,x0,x1)) return false;
d=x0;
x0=(-b+d+p)*inv(a+a,p)%p; x1=(-b-d+2*p)*inv(a+a,p)%p;
return true;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?