知識點簡單總結——二次剩餘
知識點簡單總結——二次剩餘
二次剩餘
給定 $ n,p $ ,求 $ a,0 \le a < p,a^{2} \equiv n (mod \ p) $ ,其中 $ p $ 為奇素數。
尤拉準則
判斷二次剩餘的存在性。
眾所周知根據費馬小定理有 $ n^{p-1} \equiv 1 (mod \ p) $ 。
那麼就有 $ ( n ^ { \frac{ p-1 }{ 2 } } ) ^ {2} - 1 \equiv 0 (mod \ p) $ 。
很明顯 $ n ^ { \frac{ p-1 }{ 2 } } $ 只可能是 $ 1 $ 或 $ -1 $ 。
性感理解可以證明只有上式結果為 $ 1 $ 時有二次剩餘(被打)
利用原根容易證明上式為 $ 1 $ 是存在二次剩餘的充要條件。
Cipolla演算法
隨機找到一個 $ a , a^{2} - n $ 非二次剩餘,期望大約 $ 2 $ 次找到符合條件的 $ a $ 。
設 $ i ,i ^ {2} \equiv a ^ {2} - n $ 。
定義成類似虛部的東西。
有 $ (a+i) ^ {p+1} \equiv n $ ,證明:
引理1: $ i^{p}\equiv - i $ ,證明: $ i^{p} = i( a^{ 2 } - n )^{ \frac{p-1}{2} } \equiv - i $ 。
引理2: $ (A+B)^{p} \equiv A^{p} + B^{p} $ ,證明略。
於是有 $ (a+i)^{p+1} \equiv ( a^{p} + i^{p} )(a+i) \equiv (a-i)(a+i) \equiv n $ 。
只需要求出 $ (a+i)^{ \frac{p+1}{2} } $ 即可。
至於為什麼結果不含虛部,參見https://kewth.github.io/2019/10/21/二次剩餘/
注意二次剩餘可能有兩個,互為相反數。
#include<bits/stdc++.h> using namespace std; typedef long long lint; template<typename TP>inline void read(TP &tar) { TP ret=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){ret=ret*10+(ch-'0');ch=getchar();} tar=ret*f; } struct pat{lint x,y;pat(lint x=1,lint y=0):x(x),y(y){}}; pat mul(pat a,pat b,lint w,lint p){return pat((a.x*b.x%p+a.y*b.y%p*w%p)%p,(a.x*b.y%p+a.y*b.x%p)%p);} namespace RKK { lint fpow(lint a,lint b,lint p){lint ret=1;while(b){if(b&1ll)ret=ret*a%p;a=a*a%p,b>>=1;}return ret;} lint fpow(pat a,lint b,lint w,lint p){pat ret;while(b){if(b&1ll)ret=mul(ret,a,w,p);a=mul(a,a,w,p),b>>=1;}return ret.x;} lint fuck(lint n,lint p) { if(p==2) return n%p; n%=p;if(fpow(n,p-1>>1,p)==p-1) return -1; lint a,w; while(1) { a=rand(),w=(a*a%p-n+p)%p; if(fpow(w,p-1>>1,p)==p-1) break; } return fpow(pat(a,1),p+1>>1,w,p); } int main() { srand(time(NULL)); int TAT;read(TAT);while(TAT--) { lint n,p;read(n),read(p);if(!n){puts("0");continue;} lint ans=fuck(n,p);if(ans==-1){puts("Hola!");continue;} if(p-ans<ans) ans=p-ans; printf("%lld ",ans);if(p-ans!=ans) printf("%lld",p-ans);putchar('\n'); } return 0; } } int main(){return RKK::main();}
應用
不知道(?)