來自各個大佬的講解與證明:
二次剩余Cipolla算法學習筆記 - bztMinamoto - 博客園
[數論]二次剩余及計算方法 – Miskcoo's Space
淺談二次剩余 - stevensonson的博客 - CSDN博客
二次剩余入門 - Eiffel的博客 - CSDN博客
圖文]第4章二次同余方程 - 百度文庫
二次剩余Cipolla算法學習小記 - 待成熟的葡萄 - CSDN博客
發現自己再回來看,忘了是怎么弄了,還是寫一下自己的體會,方便理解。
首先二次剩余是什么?也就是a∈Z,gcd(a,m)=1,如果x2Ξa(mod m)有解,那么a就是模m的二次剩余
當m小的時候,我們直接把0,1,2,..m/2代入計算就可以判斷有沒有解,而當m很大的時候,如果m是合數的時候比較難解決,所以我們就只來討論m是奇素數p的時候。
有個結論,歐拉判別條件:
a是模p的二次剩余的充要條件是a(p-1)/2Ξ1(mod p)
a是模p的非二次剩余的充要條件是a(p-1)/2Ξ-1(mod p)
且a是p的二次剩余的時候,同余方程恰好有兩個解
證明是不會的,這輩子都不可能去證明了的。想看詳細證明的可以翻一翻上面的博客。
然后平方剩余的一些性質
若a1,a2都是模p的二次剩余,a1*a2也是模p的二次剩余
若a1,a2都是模p的非二次剩余,a1*a2是模p的二次剩余
若a1是模p的二次剩余,a2是模p的非二次剩余,a1*a2是模p的非二次剩余
接下來有一個概念:勒讓得符號(legender symbol)
根據上面的定義就有:
所以這3個描述是等價的
概念了解得差不多了,那怎么就是怎么求x2Ξa(mod m)的解x
就很霸道的一句話
設b滿足b2-a是模p的非二次剩余,設ω=√b2-a,那么x≡(b+ω)(p+1)/2是x2Ξa(mod m)的解
理解是話,按照設定w是模p不能開根號的,那我們非要給w開根號,那么它所在的值域就變了,我們假設一個域為Fp2,那這其實是一個類似復數域的存在,
所以這里理解的話ω,可以視為復數的那個虛部的i,Fp2域的數就可以表示為x+yω,且這個域是滿足其他域的性質,也可以四則運算。那么,x≡(b+ω)(p+1)/2也就是一個合法的數
然后為什么這就是同余方程的解呢。
用以下幾個定理來解釋
定理1.ωpΞ-ω (mod p)
證明:ωp=ω*ωp-1=ω*(ω2)(p-1)/2=ω*(b2-a)(p-1)/2=ω*-1 (mod p)
定理2.(a+b)n=an+bn (mod p)
證明:二項式展開就有,然后除了i=0以及i=n時,Cin=1,其余的mod n等於0
那么x≡(b+ω)(p+1)/2就有
x2≡(b+ω)p+1≡(b+ω)p*(b+ω)
由第二個定理就有x2≡(bp+ωp)*(b+ω)
由費馬小定理bp-1≡1 (mod p)以及定理1有x2≡(b-ω)*(b+ω)
最后 x2≡b2-ω2=b2-(b2-a)=a (mod p)
所以x≡(b+ω)(p+1)/2是x2Ξa(mod m)的解
剩下的為什么這個Fp2域的解是我們要求的解,我就不會證明了,可以看上面的最后那個博客,有提到,以及時間復雜度的分析。
然后在實際實現中,ω的作用就是在於(x1+y1ω)*(x2+y2ω) 時,類似實部的地方為x1*x2+y1*y2*ω2
所以直接可以讓ω為b2-a,b的話就是通過隨機數得到,這個期望值是2。
直接來一個裸題:http://acm.timus.ru/problem.aspx?space=1&num=1132

1 #include<cstdio> 2 #include<cstdlib> 3 #include<ctime> 4 struct Ima{ 5 int x,y; 6 }; 7 int p,w; 8 Ima muli(const Ima &i1,const Ima &i2){ 9 Ima ans; 10 ans.x=(i1.x*i2.x%p+i1.y*i2.y%p*w%p)%p; 11 ans.y=(i1.x*i2.y%p+i1.y*i2.x%p)%p; 12 return ans; 13 } 14 Ima powi(Ima a,int b){ 15 Ima ans; 16 ans.x=1,ans.y=0; 17 while(b){ 18 if(b&1) ans=muli(ans,a); 19 a=muli(a,a); 20 b>>=1; 21 } 22 return ans; 23 } 24 int poww(int a,int b){ 25 int ans=1; 26 a%=p; 27 while(b){ 28 if(b&1) ans=ans*a%p; 29 a=a*a%p; 30 b>>=1; 31 } 32 return ans; 33 } 34 int Cipolla(int n){ 35 if(p==2) return 1; 36 if(poww(n,(p-1)>>1)+1==p) return -1; 37 int a; 38 while(true){ 39 a=rand()%p; 40 w=((a*a%p-n)%p+p)%p; 41 if(poww(w,(p-1)>>1)+1==p) break; 42 } 43 Ima ans; 44 ans.x=a,ans.y=1; 45 ans=powi(ans,(p+1)>>1); 46 return ans.x; 47 } 48 int main(){ 49 int t,n,ans1,ans2; 50 srand(time(NULL)); 51 scanf("%d",&t); 52 while(t--){ 53 scanf("%d%d",&n,&p); 54 n%=p; 55 ans1=Cipolla(n),ans2=p-ans1; 56 if(ans1==-1) printf("No root\n"); 57 else if(ans1==ans2) printf("%d\n",ans1); 58 else if(ans1<ans2) printf("%d %d\n",ans1,ans2); 59 else printf("%d %d\n",ans2,ans1); 60 } 61 return 0; 62 }
還有牛客多校的一題:Quadratic equation

1 #include<cstdio> 2 #include<cstdlib> 3 #include<ctime> 4 typedef long long ll; 5 const ll p=1e9+7; 6 struct Ima{ 7 ll x,y; 8 }; 9 ll w; 10 Ima muli(const Ima &i1,const Ima &i2){ 11 Ima ans; 12 ans.x=(i1.x*i2.x%p+i1.y*i2.y%p*w%p)%p; 13 ans.y=(i1.x*i2.y%p+i1.y*i2.x%p)%p; 14 return ans; 15 } 16 Ima powi(Ima a,ll b){ 17 Ima ans; 18 ans.x=1,ans.y=0; 19 while(b){ 20 if(b&1) ans=muli(ans,a); 21 a=muli(a,a); 22 b>>=1; 23 } 24 return ans; 25 } 26 ll poww(ll a,ll b){ 27 ll ans=1; 28 a%=p; 29 while(b){ 30 if(b&1) ans=ans*a%p; 31 a=a*a%p; 32 b>>=1; 33 } 34 return ans; 35 } 36 ll Cipolla(ll n){ 37 if(n==0) return 0; 38 if(n==1) return 1; 39 if(poww(n,(p-1)>>1)+1==p) return -1; 40 ll a; 41 while(true){ 42 a=rand()%p; 43 w=((a*a%p-n)%p+p)%p; 44 if(poww(w,(p-1)>>1)+1==p) break; 45 } 46 Ima ans; 47 ans.x=a,ans.y=1; 48 ans=powi(ans,(p+1)>>1); 49 return ans.x; 50 } 51 void solve(ll b,ll c){ 52 ll n=((b*b%p-4*c%p)%p+p)%p; 53 ll a=Cipolla(n),x,y; 54 if(a==-1){ 55 printf("-1 -1\n"); 56 return ; 57 } 58 if(!((a+b)&1)) y=(a+b)/2,x=b-y; 59 else y=(a+b+p)/2,x=b+p-y; 60 x=(x+p)%p; 61 y=(y+p)%p; 62 if(x>y) printf("%lld %lld\n",y,x); 63 else printf("%lld %lld\n",x,y); 64 } 65 int main(){ 66 int t; 67 ll b,c; 68 srand(time(NULL)); 69 scanf("%d",&t); 70 while(t--){ 71 scanf("%lld%lld",&b,&c); 72 solve(b,c); 73 } 74 return 0; 75 }
然后補充一下關於勒讓得的一些性質
剩下的合數的還有其他補充內容就之后再更。