\(Cipolla\)好像是個很厲害的東西……雖然我覺得這東西直接用離散對數+\(bsgs\)艹過去也可以……
如無特殊說明,以下均默認\(p\)為模數,且\(p\)為奇素數
如無特殊說明,以下均認為運算在\(\mathbb{F}_p\)下進行(元素為\(0\)到\(p-1\)這\(p\)個元素,運算為模\(p\)意義下的加減乘除)
定義
二次剩余
對於\(P,n\),若存在\(x\),滿足
則稱\(n\)為模\(P\)意義下的二次剩余
用人話說就是\(n\)在模\(p\)意義下是否能開方
勒讓德符號
定義如下
歐拉判別准則
對於勒讓德符號,有
證明:
若\(n\equiv 0\pmod{p}\),顯然該柿子成立
若\(n\)在模\(p\)意義下是二次剩余,即存在\(x^2\equiv n\pmod{p}\),那么就有\(x^{p-1}\equiv 1\pmod{p}\),根據費馬小定理顯然\(x\)存在
若\(n\)在模\(p\)意義下是二次非剩余,我們仍假設存在\(x^2\equiv n\pmod{p}\),從而有\(x^{p-1}\equiv -1\pmod{p}\),由費馬小定理,顯然\(x\)不存在
定理們
- 定理一:
-
證明:把后面的平方展開即可
-
定理二:\(p\)的二次剩余和二次非剩余的個數均為\({p-1\over 2}\)(不考慮\(0\))的情況下
-
證明:我們只考慮所有的\(n^2\),假設有\(x\neq y\)且\(x^2\equiv y^2\pmod{p}\),則\(p\mid (x^2-y^2)\),即\(p\mid (x-y)(x+y)\),顯然\(p\nmid(x-y)\),則\(p\mid(x+y)\),故\(x+y\equiv 0\pmod{p}\),就是定理一的情況。也就是說不同的\(x^2\)共有\({p-1\over 2}\),二次剩余也為\({p-1\over 2}\),減一減就可知二次非剩余個數也是\({p-1\over 2}\)
算法流程
先寫過程再來證明好了……
簡單來說就是我們需要對一個數開方(根據定理\(1\)顯然有解的情況下必定有\(2\)個解,下面算法求出的是其中隨機一個解,另一個只要用\(p-n\)計算即可)
\(1.\)判斷給定的數\(x\)是否是二次剩余,如果不是就返回\(-1\)表示無解(如果是\(0\)的話直接返回\(0\))
\(2.\)隨機一個\(a\),使其滿足\((a^2-x)\)是二次非剩余(根據定理\(2\),期望隨機次數\(2\)次)
\(3.\)令\(\omega\equiv (a^2-x)\pmod{p}\),取\(y\equiv \left(a+{\omega}\right)^{p+1\over 2}\)即為其中一個可行解,返回即可
證明
雖然上面這個算法流程簡直漏洞百出……但是神奇的是它的確是對的
第一個問題,你不是都說了\((a^2-x)\)是二次非剩余么?就是說\((a^2-x)\)在模\(p\)意義下是不能開方的,那\(\omega\)是個什么東西?
關於這個問題,我們可以類比一下虛數單位元\(i=\sqrt{-1}\),即我們需要把這個域給擴充一下,將其擴充為\(\mathbb{F}_{p^2}\),其中的每一個元素都形如\(a+b\omega\)
我們把復數域上的四則運算全都扔到\(\mathbb{F}_{p^2}\)上面,顯然滿足封閉性、交換律、結合律以及分配律,還存在加法零元和乘法逆元(貌似符合環的定義)(有興趣的可以看看下面這張圖)
所以理論上來說這個開方的確是沒有問題的……
第二個問題,為什么\(y\)就是一個可行的解呢?
這個問題,我們先需要一些定理
- 定理三:
- 證明:
- 定理四:
- 證明:二項式定理展開,對於每一項前面的組合數\({p\choose i}\),因為\(p\)是奇素數,所以只有當\(i=0\)或\(i=p\)時它沒有\(p\)這個因子,化簡之后可得
然后就是大力頹柿子的時間~~~
最后一個問題,我們需要的解是在\(\mathbb{F}_p\)意義下的,而你求出的解是\(\mathbb{F}_{p^2}\)意義下的。用人話說的話,就是你確定求出的解的虛部為\(0\)么?
這個問題的話,首先根據拉格朗日定理,我們知道在任意一個模\(p\)的數域下,一個\(n\)次多項式最多只有\(n\)個根。由於\(\mathbb{F}_{p^2}\)是對數域\(\mathbb{F}_p\)的擴充,所以\(\mathbb{F}_p\)的兩根在數域\(\mathbb{F}_{p^2}\)也一定有效。並且我們知道這里根最多只有\(2\)個,1所以我們求出的解必定是\(\mathbb{F}_p\)下的根,也就是虛部為\(0\)
然后沒有然后了,問題解決
代碼
int w,a;
struct cp{
int x,y;
inline cp(R int _x,R int _y):x(_x),y(_y){}
inline cp operator *(const cp &b)const{
return cp(add(mul(x,b.x),mul(w,mul(y,b.y))),add(mul(x,b.y),mul(y,b.x)));
}
};
int ksm(R cp x,R int y){
R cp res(1,0);
for(;y;y>>=1,x=x*x)if(y&1)res=res*x;
return res.x;
}
int Sqrt(int x){
if(!x)return 0;
if(ksm(x,(P-1)>>1)==P-1)return -1;
while(true){
a=mul(rand(),rand()),w=dec(mul(a,a),x);
if(ksm(w,(P-1)>>1)==P-1)return ksm(cp(a,1),(P+1)>>1);
}
}
如果要題目的話,可以去做一下這道(\(bsgs+Cipolla\))
參考文獻