目录
- 二次剩余学习笔记
- 二次剩余
- 勒让德符号
- cipolla算法
- 代码实现
二次剩余学习笔记
看SCOI2018的时候发现不会这个东西
来写模板与简单证明
p是奇质数,偶的就那一个,就不讨论了
二次剩余
求解\(x^2\equiv n \pmod p\)
勒让德符号
\(a^\frac{P-1}{2}\equiv \left(\frac{a}{p}\right) \pmod p\)
令\(x^2\equiv a \pmod p\)
存在x时\(x^{p-1}\equiv 1 \pmod p\)
所以\(x\equiv a^{\frac{1}{2}}\pmod p\),\(x^{p-1}\equiv a^{\frac{p-1}{2}}\equiv 1\pmod p\)
不存在x时\(a^{\frac{p-1}{2}}\)不能被表示成\(x^{p-1}\) ,于是这个东西就不等于1,又因为它的平方等于1,所以它就等于-1(膜意义下)
cipolla算法
首先随机a使得\(a^2-n\)不是二次剩余
令\(\omega =\sqrt{a^2-n}\),作为当前数域下单位根
由于满足一系列性质,这个数域是成立的
又因为\(\omega^2\)不是二次剩余,由勒让德符号可知\((w^2)^{\frac{p-1}{2}}\equiv w^{p-1}\not \equiv 1\pmod p\)
由费马小定理,\((w^2)^{p-1}\equiv 1 \pmod p\)
所以\(w^{p-1}\equiv -1\)
于是就可以搞事情了
有这样一个式子
\[ (a+w)^{p+1} \]
\[ \equiv(a+w)^p(a+w) \]
\[ \equiv(\sum_i a^iw^{p-i}C^i_p)(a+w) \pmod p \]
因为\(i!=p,0\)时组合数的阶乘p无法消掉(奇质数),所以\(mod~p\)之后为0
\[ \equiv (a^p+w^p)(a+w) \pmod p \]
由上面的结论和费马小定理:
\(w^{p-1}\equiv -1\)
\(a^{p-1}\equiv 1\)
有
\[ \equiv (a-w)(a+w) \pmod p \]
\[ \equiv a^2-w^2\pmod p \]
\[ \equiv a^2-(a^2-n)\pmod p \]
\[ \equiv n \pmod p \]
整理得到
\[ (x^2)\equiv (a+w)^{\frac{p+1}{2}*2}\equiv (a+w)^{p+1}\equiv n \pmod p \]
即\(x \equiv \pm (a+w)^{\frac{p+1}{2}}\)
可以证明其不含\(w\)
代码实现
#include
#include
#include
#include
#include
#define ll long long
using namespace std;
ll read(){
ll x=0,pos=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return pos?x:-x;
}
ll mod,n;
ll i2;
struct complex{
ll r,i;
complex(ll r=0,ll i=0):r(r),i(i){}
int operator == (complex y){
return r==y.r&&i==y.i;
}
complex operator *(complex y){
return complex((r*y.r%mod+i*y.i%mod*i2%mod)%mod,((r*y.i%mod+i*y.r)%mod)%mod);
}
};
complex ksm(complex a,int b){
complex res=complex(1,0);
while(b){
if(b&1) res=res*a;
a=a*a;
b>>=1;
}
return res;
}
int check(int x){
return ksm(complex(x,0),(mod-1)/2)==1;
}
int R(int now){
return ((rand()<<15)+rand())%(now-1)+1;
}
void solve(){
ll a=R(mod);
while(check((a*a+mod-n)%mod)) a=R(mod);
i2=(a*a%mod-n+mod)%mod;
ll x0=ksm(complex(a,1),(mod+1)/2).r;
printf("%lld ",min(x0,mod-x0));
printf("%lld\n",max(x0,mod-x0));
}
int main(){
int T=read();
while(T--){
n=read(),mod=read();
if(n==0){
printf("%d\n",0);continue;
}
if(!check(n)) printf("Hola!\n");
else solve();
}
return 0;
}