二次剩余
二次剩余
PS:以下内容大概都是抄的。
大概是解这样一个方程:
这里讨论 \(p\) 为奇素数。
称非 \(0\) 数 \(n\) 是二次剩余当 \(n\) 存在上述方程的解,反之成为非二次剩余。
二次剩余的数量为 \(\frac{p-1}{2}\),原因是这样的:
1.若一个数 \(n\) 为二次剩余,设它有两组解,\(x_1,x_2\)。
那么 \(x_1^2 \equiv x_2^2 \equiv n\),由平方差公式得到 \((x_1-x_2)(x_1+x_2) \equiv 0\)。
那么 \(x_1 \equiv -x_2\),因为 \(p\) 为奇素数,所以 \(x_1 \neq x_2\)。
2.若 \(n\) 为二次剩余,存在解 \(x\),\(-x\) 也为解。
欧拉准则
大概是判断一个数 \(n\) 是否是二次剩余。
做法是这样的,求 \(n^{\frac{p-1}{2}}\),若为 \(1\) 则是,否则不是。
首先有这样一个事情,\(n^{p-1} \equiv 1\),则 \((n^{\frac{p-1}{2}})^2 \equiv 1\)。
若其取值为 \(1\),可以令 \(n=g^k\),其中 \(g\) 为原根。
则有 \(g^{k\frac{p-1}{2}} \equiv 1\),即 \((p-1)| k\frac{p-1}{2}\)。
即 \(2|k\),所以 \(n\) 为二次剩余。
然后还有这样一个事情,若 \(n\) 为二次剩余,则存在 \(x^2 \equiv n\)。
即 \(n^{\frac{p-1}{2}}\equiv x^{p-1} \equiv 1\)。
所以 \(n^{\frac{p-1}{2}}\equiv 1\) 与 \(n\) 是二次剩余是等价的条件。
故可以通过 \(n^{\frac{p-1}{2}}\) 为 \(1\) 或 \(-1\) 来判断 \(n\) 是否为二次剩余。
Cipolla
大概是对于 \(n\) 为二次剩余,找到 \(x\) 满足 \(x^2 \equiv n\)。
做法是这样的,首先找到一个 \(a\),满足 \(a^2-n\) 为非二次剩余,这个过程通过随机并检验即可实现。
定义一个类似虚部的东西 \(i\),满足 \(i^2 \equiv a^2-n\)。
然后有 \((a+i)^{p+1} \equiv n\),\((a+i)^{\frac{p+1}{2}}\equiv x\)。
有这样一些事情:
1.\((A+B)^p=A^p + B^p\)
通过二项式定理展开为 \(\sum \limits_{j=0}^{p} A^j B^{p-j} \binom{p}{j}\)。
其中 \(\binom{p}{j}\) 中的质因子 \(p\) 只在 \(j=0,j=p\) 时不存在。
这样的话考虑 \((a+i)^{p+1}\) 这个东西。
可以展开为 \((a+i)^p(a+i)=(a^p+i^p)(a+i)=(a-i)(a+i)=a^2-i^2=n\)
故 \((a+i)^{\frac{p+1}{2}}\) 和 \(-(a+i)^{\frac{p+1}{2}}\) 是两个解。
可以证明 \((a+i)^{\frac{p+1}{2}}\) 不存在虚部。
假设虚部存在,则有 \((x+yi)^2=n,y\neq 0\)。
展开得 \(x^2+y^2(a^2-n)-n=2xy\)。
其中左侧不含虚部,故 \(y\neq 0,x=0\)。
代入得到 \(y^2i^2=n\),显然无解,与假设不成立。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int mod,n,a;
struct node{
ll r,i;
node(int r=0,int i=0):r(r),i(i){}
};
inline node operator * (const node &x,const node &y){
return node((x.r*y.r%mod+x.i*y.i%mod*(1ll*a*a%mod-n+mod))%mod,(x.r*y.i+x.i*y.r)%mod);
}
inline ll qpow(ll x,int k,ll r=1){
for(;k;k>>=1,x=x*x%mod) if(k&1) r=r*x%mod;
return r;
}
inline node qpow(node x,int k,node r=node(1,0)){
for(;k;k>>=1,x=x*x) if(k&1) r=r*x;
return r;
}
bool check(int x){
return qpow(x,mod-1>>1)==1;
}
int main(){
int T; scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&mod);
if(!n){ puts("0"); continue; }
if(!check(n)){ puts("Hola!"); continue; }
for(a=rand()%(mod-1)+1;check((1ll*a*a-n+mod)%mod);a=rand()%(mod-1)+1);
int x=qpow(node(a,1),mod+1>>1).r,y=mod-x; if(y<x) swap(x,y);
printf("%d %d\n",x,y);
}
return 0;
}