题意
给定一个正整数 n n n,解方程
x 2 ≡ n ( m o d p ) x^2 \equiv n \pmod{p} x2≡n(modp)
这里只考虑 p p p 为奇素数的情况。
解的个数
假设对于 n n n 有多个解,其中两个为 x 1 x_1 x1 和 x 2 x_2 x2。
那么有
x 1 2 − x 2 2 ≡ 0 ( m o d p ) x_1^2 - x_2^2 \equiv 0\pmod{p} x12−x22≡0(modp)
( x 1 − x 2 ) ( x 1 + x 2 ) ≡ 0 ( m o d p ) (x_1 - x_2)(x_1 + x_2) \equiv 0\pmod{p} (x1−x2)(x1+x2)≡0(modp)
因为 x 1 ≠ x 2 x_1 \ne x_2 x1=x2,那么 x 1 x_1 x1 和 x 2 x_2 x2 就一定是相反数,所以对于一个 n n n 要么有两个解,要么无解。
如果对于 n n n 有两个解,则称它为二次剩余,否则为二次非剩余。
顺带一提,因为一对相反数只能构成一个二次剩余,所以二次剩余的个数为 p − 1 2 \frac{p-1}{2} 2p−1 个。
Legendre 符号
( a p ) = { 0 , p ∣ a , 1 , ( p ∤ a ) ∧ ( ( ∃ x ∈ Z ) , x 2 ≡ a ( m o d p ) ) , − 1 , o t h e r w i s e . \left(\frac{a}{p}\right)=\left\{\begin{array}{l} 0, & p\mid a, \\ 1, & (p\nmid a) \wedge ((\exists x \in Z), x^2\equiv a\pmod{p}), \\ -1, & otherwise.\\ \end{array}\right. (pa)=⎩ ⎨ ⎧0,1,−1,p∣a,(p∤a)∧((∃x∈Z),x2≡a(modp)),otherwise.
欧拉准则
对于奇素数 p p p 和正整数 a a a,有
a p − 1 2 ≡ ( a p ) ( m o d p ) a^{\frac{p-1}{2}} \equiv \left(\frac{a}{p}\right) \pmod{p} a2p−1≡(pa)(modp)
接下来我们证明一下这个结论,设 g g g 为 p p p 的一个原根,且有 g q ≡ a ( m o d p ) g^q \equiv a \pmod{p} gq≡a(modp)。
引理1
若 a a a 为二次剩余,当且仅当 q q q 为偶数。
先证明充分性
若 q q q 为偶数,那么就有一个解为 g q 2 g^{\frac{q}{2}} g2q,即 a a a 为二次剩余。
再证明必要性
设 x = g y x = g^{y} x=gy 为 x 2 ≡ a ( m o d p ) x^2\equiv a\pmod{p} x2≡a(modp) 的一个解,那么就有
g 2 y ≡ g q ( m o d p ) g^{2y} \equiv g^q \pmod{p} g2y≡gq(modp)
2 y ≡ q ( m o d φ ( p ) ) 2y \equiv q\pmod{\varphi(p)} 2y≡q(modφ(p))
因为 2 y 2y 2y 和 φ ( p ) \varphi(p) φ(p) 都是偶数,所以 q q q 也一定是偶数。
证明
那么有
a p − 1 2 ≡ ( g p − 1 2 ) q ≡ ( − 1 ) q ( m o d p ) a^{\frac{p-1}{2}} \equiv (g^{\frac{p-1}{2}})^q \equiv (-1)^q \pmod{p} a2p−1≡(g2p−1)q≡(−1)q(modp)
根据引理1得出的结论,若 a a a 为二次剩余,则 q q q 为偶数,否则为奇数,所以 ( − 1 ) q ≡ ( a p ) ( m o d p ) (-1)^q \equiv \left(\frac{a}{p}\right) \pmod{p} (−1)q≡(pa)(modp)。
证毕。
Cipolla 算法
我们先找到一个 a a a,使得 a 2 − n a^2 - n a2−n 是二次非剩余,由于二次非剩余出现概率为 1 2 \frac{1}{2} 21,所以用随机数很容易得到一个合适的 a a a。
根据欧拉准则,有 ( a 2 − n ) p − 1 2 ≡ − 1 ( m o d p ) (a^2-n)^{\frac{p-1}{2}} \equiv -1 \pmod{p} (a2−n)2p−1≡−1(modp)。
我们重新定义一个虚数概念,将虚数单位 i i i 定义为 i 2 ≡ a 2 − n ( m o d p ) i^2 \equiv a^2 - n\pmod{p} i2≡a2−n(modp),将所有虚数表示为 A + B i A + Bi A+Bi 的形式( A , B ∈ R A,B\in R A,B∈R),并且认为模 p p p 意义下的虚数为对于 A , B A,B A,B 分别取模。
引理1
i p ≡ − i ( m o d p ) i^p\equiv -i\pmod{p} ip≡−i(modp)
证明: i p ≡ i ( i 2 ) p − 1 2 ≡ i ( a 2 − n ) p − 1 2 ≡ − i ( m o d p ) i^p \equiv i (i^2)^{\frac{p-1}{2}} \equiv i (a^2 - n)^{\frac{p-1}{2}} \equiv -i \pmod{p} ip≡i(i2)2p−1≡i(a2−n)2p−1≡−i(modp)
引理2
( A + B ) p ≡ A p + B p ( m o d p ) (A+B)^p \equiv A^p + B^p \pmod{p} (A+B)p≡Ap+Bp(modp)
证明:运用二项式定理,只有 C p 0 C_p^0 Cp0 和 C p p C_p^p Cpp 不是 p p p 的倍数,所以等于 A p + B p A^p + B^p Ap+Bp。
有了以上引理,进行计算 ( a + i ) p + 1 ≡ ( a + i ) p ( a + i ) ≡ ( a p + i p ) ( a + i ) ≡ ( a − i ) ( a + i ) ≡ a 2 − i 2 ≡ n ( m o d p ) (a+i)^{p+1} \equiv (a+i)^p (a+i) \equiv (a^p + i^p)(a+i) \equiv (a-i)(a+i) \equiv a^2 - i^2 \equiv n \pmod{p} (a+i)p+1≡(a+i)p(a+i)≡(ap+ip)(a+i)≡(a−i)(a+i)≡a2−i2≡n(modp),于是我们得到一个解 ( a + i ) p + 1 2 (a+i)^{\frac{p + 1}{2}} (a+i)2p+1。
这时我们有一个疑问,这个数一定是实数吗?
运用反证法,设存在一个虚数解 A + B i A + Bi A+Bi 满足方程 ( A + B i ) 2 ≡ n ( m o d p ) ( B ≠ 0 ) (A+Bi)^2 \equiv n \pmod{p}(B\ne 0) (A+Bi)2≡n(modp)(B=0),
那么有 A 2 + B 2 i 2 + 2 A B i ≡ n ( m o d p ) A^2 + B^2i^2 + 2ABi \equiv n \pmod{p} A2+B2i2+2ABi≡n(modp),
A 2 + B 2 ( a 2 − n ) − n ≡ 2 A B i ( m o d p ) A^2 + B^2(a^2 - n) - n \equiv 2ABi \pmod{p} A2+B2(a2−n)−n≡2ABi(modp)
由于方程左边不是虚数,所以右边也一定不是虚数,即 A = 0 A=0 A=0,于是 ( B i ) 2 ≡ n ( m o d p ) (Bi)^2 \equiv n\pmod{p} (Bi)2≡n(modp),
得到 i 2 ≡ n B − 2 ( m o d p ) i^2 \equiv n B^{-2}\pmod{p} i2≡nB−2(modp) 由于 n n n 和 B − 2 B^{-2} B−2 均为二次剩余,所以右边一定为二次剩余,而左边不是,矛盾,假设不成立。
所以得出的解一定是实数。
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll I_mul_I, mod;
struct Complex {
ll a, b;
Complex operator* (Complex y) {
return {(a * y.a % mod + b * y.b % mod * I_mul_I % mod) % mod, (a * y.b % mod + b * y.a % mod) % mod};
}
};
ll qpow(ll x, ll y) {
ll res = 1;
while (y) {
if (y & 1) res = res * x % mod;
x = x * x % mod;
y >>= 1;
}
return res;
}
Complex qpow(Complex x, ll y) {
Complex res = {1, 0};
while (y) {
if (y & 1) res = res * x;
x = x * x;
y >>= 1;
}
return res;
}
bool legendre(ll x) {
return qpow(x, (mod - 1) >> 1) == 1;
}
ll cipolla(ll n, ll p) {
n %= p, mod = p;
if (!n) return 0;
if (!legendre(n)) return -1;
ll a = rand() % mod;
while (legendre((a * a + mod - n) % mod)) a = rand() % mod;
I_mul_I = (a * a + mod - n) % mod;
return qpow(Complex({a, 1}), (mod + 1) >> 1).a;
}
int main() {
srand(time(0));
int T;
scanf("%d", &T);
while (T -- ) {
ll x, p;
scanf("%lld%lld", &x, &p);
ll ans = cipolla(x, p);
if (ans == -1) puts("Hola!");
else if (ans == 0) puts("0");
else {
if (ans > p - ans) ans = p - ans;
printf("%lld %lld\n", ans, p - ans);
}
}
return 0;
}