今天做题的时候发现要求个二次剩余,突然发现自己不会求(x
跑去学习了一波,写个笔记防止自己又忘掉。
顺便这里的内容只有模奇素数意义下的,别的咕了(x
- 勒让德符号
首先我们知道,如果 (x^2equiv npmod p),那么 ((p-x)^2equiv npmod p)。这也就是说,由于平方根是成对出现的,([1, p-1]) 中拥有模意义下平方根的数(这样的数称作 (p) 的二次剩余)最多只有 (frac{p-1}{2}) 个。实际上,设 (u e vin[1, p-1]),且 (u^2equiv v^2pmod p),则 ((u+v)(u-v)equiv 0pmod p)。由于 (u e v),不可能有 (p|(u-v)),所以只能是 (p|(u+v))。再加上 (u+ vin [2, 2p-2]),可以知道 (u+v=p)。所以对于任意的 (uin[1, p-1]),只有唯一的 (v) 满足 (u^2equiv v^2),所以 (p) 的二次剩余恰有 (frac{p-1}{2}) 个。
因此可以引入勒让德符号的概念,它可以判断一个数 (n) 是否是奇素数 (p) 的二次剩余。
设 (left(frac{n}{p} ight)equiv n^{frac{p-1}{2}}pmod p),则 (left(frac{n}{p} ight)) 的取值只会有三种可能:
(left(frac{n}{p} ight)equiv 0),则 (nequiv 0pmod p),正确性显然。
(left(frac{n}{p} ight)equiv 1),则 (n) 是 (p) 的二次剩余;
(left(frac{n}{p} ight)equiv -1),则 (n) 不是 (p) 的二次剩余。
对于 $2$ 的情况,设 (x^2equiv n),则 (left(frac{n}{p} ight)equiv x^{p-1}pmod p),由费马小定理可知正确。
否则,不存在 (x^2equiv n),也就意味着,对于任意的 (iin [1, p-1]),都有唯一确定的 (j e i),使得 (icdot jequiv npmod p)。我们可以将这样的数分成互不相交的 (frac{p-1}{2}) 对,从而得到 ((p-1)!equiv n^{frac{p-1}{2}}pmod p)。由威尔逊定理,可以知道 (left(frac{n}{p} ight)equiv -1pmod p)。
(实际上上面的证明只是表达了一下它们之间的联系,充分性和必要性并没有分别证明……不过感性理解一下,直接硬点它是充要的就好了x
- 二次剩余的求法
如果已经知道了 (n) 是二次剩余,考虑如何求出它的平方根。
设 (ain [1, p-1], omega=a^2-n)。如果 (left(frac{omega}{p} ight)equiv -1),则 ((a+sqrt{omega})^{frac{p+1}{2}}) 是 (n) 的一个平方根。
- ¿
你不是刚说了 (left(frac{omega}{p} ight)equiv -1) 吗,怎么又求起平方根来了?
还记得 (sqrt{-1}) 吗?它虽然无法在实数域里定义,但是我们仍然能把 (sqrt{-1}=i) 代入到我们的运算中。
类似地,我们可以定义 (i_{omega}=sqrt{omega}),它满足 (i_{omega}^2=omega),只是不存在于模 (p) 的整数域中,这并不妨碍我们利用它进行求解。
所以,先来证明 ((a+sqrt{omega})^{p+1}equiv npmod p)。
对于 ((3)) 处,由于 (p) 是素数,(inom{p}{i}) 中,只有 (i=0) 和 (i=p) 的情况下,不包含 (p) 这个因子。
对于 ((4)) 处,(a^pequiv a) 可以由费马小定理得到,而 (sqrt{omega}^pequiv sqrt{omega}cdot omega^{frac{p-1}{2}}equiv sqrt{omega}left(frac{omega}{p} ight)equiv -sqrt{omega}pmod p)。
注意对于 (sqrt{omega}) 的运算中,有 ((a+bsqrt{omega})cdot (c+dsqrt{omega})=(ac+bdomega)+(bc+ad)sqrt{omega}),与复数运算类似,但不完全相同。
最后要说明的一点是,如何求出满足条件的 (a) 和 (omega)。实际上直接随机在 ([0, p-1]) 内取就可以了,由于满足条件的 (omega) 占总数的一半,期望随机 $2$ 次就可以找到。
关于代码,它咕了(
struct cp {
ll r, i;
cp(ll r, ll i) : r(r), i(i) {}
inline cp operator * (const cp &x) const {
return cp(addMod(mul(r, x.r), mul(g, mul(i, x.i))), addMod(mul(r, x.i), mul(i, x.r)));
}
};
inline ll leg(ll x) {
return quickpow(x, (mod - 1) >> 1);
}
inline ll findRoot(ll x) {
if (!x) return 0;
if (leg(x) != 1) return -1;
ll a;
while (1) {
a = rnd.rnd() % mod;
if (leg(g = addMod(mul(a, a), mod - x)) == mod - 1) break;
}
cp ret(1, 0), base(a, 1);
ll pw = (mod + 1) >> 1;
while (pw) {
if (pw & 1) ret = ret * base;
base = base * base, pw >>= 1;
}
return ret.r;
}