<span>总结「二次剩余」</span>
转载注明来源:https://www.cnblogs.com/syc233/p/13741831.html
二次剩余,之前从数竞同学那听到过这个东西,觉得在OI中没啥用。直到今天T1考了二次剩余,我才流下了没有数理基础的眼泪。
二次剩余,其实就是模意义下开根。
给定常数 \(n\) ,解同余方程:
当存在 \(x\) 满足上式时,则 \(n\) 是模 \(p\) 的二次剩余。
否则 \(n\) 是模 \(p\) 的二次非剩余。
为了方便,下面的同余式均省略 \(({\rm{mod}} \ p)\) 。
基本结论
这里只讨论 \(p\) 为奇素数的情况。
两个二次剩余的乘积必然还是二次剩余
证明略。
二次剩余的逆元还是二次剩余
证明略。
满足 “ \(n\) 是模 \(p\) 的二次剩余“ 的 \(n\) 有 \(\frac{p+1}{2}\) 个
对于同余方程 \(x^2\equiv n\) ,假设有两个解 \(x_1,x_2\) ,则易证 \(x_1+x_2\equiv 0\) ,即 \(x_1\) 和 \(x_2\) 互为相反数。
那么这样的解的对数有 \(p+1\) 对,每一个 \(n\) 对应了两个解,那么 \(n\) 有 \(\frac{p+1}{2}\) 个。
欧拉准则
勒让德符号 \({\displaystyle ({\frac {a}{p}})}\) 定义:
欧拉准则:
那么若 \(n\) 是二次剩余,则 \(n^{\frac{p-1}{2}}=1\) 。
证明:
由费马小定理 \(n^{p-1}\equiv 1\) ,则 \((n^{\frac{p-1}{2}})^2 \equiv 1\) 。那么 \(n^{\frac{p-1}{2}}\) 是 \(1\) 模 \(p\) 意义下开根的结果,则 \(n^{\frac{p-1}{2}}=\pm 1\) 。
若 \(n^{\frac{p-1}{2}}=1\) ,则令 \(n=g^k\) ,其中 \(g\) 为 \(p\) 的原根。
则有 \((g^k)^{\frac{p-1}{2}}\equiv 1\) ,因为 \(g^{p-1}\equiv 1\) 且 \(g\) 是原根,那么有 \(p-1|k\times\frac{p-1}{2}\) ,则 \(k\) 是偶数, \(n\) 开根的结果是 \(g^{\frac{k}{2}}\) 。
所以 \(n^{\frac{p-1}{2}}=1\) 时,\(n\) 是二次剩余,否则 \(n\) 是二次非剩余。
证毕。
求解 \(x^2 \equiv n \ ({\rm{mod}} \ p)\)
先找出一个数 \(a\) ,满足 \(a^2-n\) 为二次非剩余,随机出一个数再判断即可。
令 \(i^2\equiv a^2-n\) ,类比复数的定义,将所有数定义成 \(A+Bi\) 的形式。
那么 \((a+i)^{\frac{p+1}{2}}\) 是原方程的一个解。
证明:
相当于证明 \((a+i)^{p+1} \equiv n\) 。
则有:
由卢卡斯定理可得:
由费马小定理有 \(a^p \equiv a\) 。则:
得证 \((a+i)^{p+1} \equiv n\) 。
证毕。
\((a+i)^{\frac{p+1}{2}}\) 中虚部一定为 \(0\) 。
证明:
假设存在 \((A+Bi)^2\equiv n,B\not =0\) ,那么:
因为式子左边虚部为 \(0\) ,所以式子右边虚部也应该为 \(0\) ,则 \(A=0\) 。
得到 \(i^2\equiv n\times B^{-2}\) ,因为右边是两个二次剩余,所以 \(i^2\) 也是二次剩余,与 \(i\) 的定义矛盾,所以 \(B=0\) 。
证毕。
\(\text{Code}:\)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define Rint register int
#define INF 0x3f3f3f3f
using namespace std;
typedef long long lxl;
template <typename T>
inline void read(T &x)
{
x=0;T f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
x*=f;
}
lxl n,p,w;
struct Complex
{
lxl x,y;
Complex(lxl x,lxl y):x(x),y(y){}
Complex(){}
inline Complex operator * (const Complex &T)const
{
return Complex((x*T.x%p+y*T.y%p*w%p)%p,(x*T.y%p+y*T.x%p)%p);
}
};
inline lxl fmi(lxl a,lxl b)
{
lxl res(1);
a%=p;
while(b>0)
{
if(b&1) (res*=a)%=p;
(a*=a)%=p;
b>>=1;
}
return res;
}
inline Complex cfmi(Complex a,lxl b)
{
Complex res(1,0);
while(b>0)
{
if(b&1) res=res*a;
a=a*a;
b>>=1;
}
return res;
}
inline lxl Sqrt(lxl n,lxl p) // 在模 p 意义下开根
{
n=(n%p+p)%p;
if(!n) return 0;
if(fmi(n,(p-1)>>1)!=1) return -1;
lxl a;
while(1)
{
a=rand()%p;
w=(a*a%p-n+p)%p;
if(fmi(w,(p-1)>>1)!=1) break;
}
return cfmi(Complex(a,1),(p+1)>>1).x;
}
int main()
{
// freopen("P5491.in","r",stdin);
int T;read(T);
while(T--)
{
read(n),read(p);
lxl x1=Sqrt(n,p),x2=(p-x1)%p;
if(!~x1) puts("Hola!");
else if(x1==x2) printf("%lld\n",x1);
else printf("%lld %lld\n",min(x1,x2),max(x1,x2));
}
return 0;
}