二次剩余,就是求解方程
x
2
≡
n
(
m
o
d
p
)
x^2\equiv n (\mod p)
x2≡n(modp)
此时
p
p
p为一个奇素数,就是常规的二次剩余问题
根的数量
我们假设一个方程有多个根那么可以列出 x 0 2 ≡ x 1 2 ( m o d p ) x_0^2\equiv x_1^2(\mod p) x02≡x12(modp)
移项之后可得 ( x 1 − x 0 ) ( x 1 + x 0 ) ≡ 0 ( m o d p ) (x_1-x_0)(x_1+x_0)\equiv0(\mod p) (x1−x0)(x1+x0)≡0(modp)
因为 x 0 x_0 x0, x 1 x_1 x1为两不同的根所以 x 1 − x 0 x_1-x_0 x1−x0模 p p p不会为 0 0 0
所以 x 1 + x 0 ≡ 0 ( m o d p ) x_1+x_0\equiv0(\mod p) x1+x0≡0(modp),即 x 0 x_0 x0, x 1 x_1 x1为相反数
因为 p p p为奇素数,所以两根肯定不相同,所以方程必然有两不同根
欧拉准则
那么我们现在解决判断 n n n是否在模 p p p的意义下为二次剩余的问题
先放结论 n p − 1 2 ≡ 1 ( m o d p ) n^{\frac{p-1}2}\equiv 1(\mod p) n2p−1≡1(modp)是 n n n为模 p p p意义下的二次剩余的充要条件
先证明必要性,由于 n n n为二次剩余由 x p − 1 ≡ 1 ( m o d p ) x^{p-1}\equiv 1(\mod p) xp−1≡1(modp)可知 ( x 2 ) p − 1 2 = n p − 1 2 ≡ 1 ( m o d p ) (x^2)^{\frac{p-1}2}=n^{\frac{p-1}2}\equiv 1(\mod p) (x2)2p−1=n2p−1≡1(modp)
再证明充分性, n = g k n=g^k n=gk,其中 g g g为原根,那么 g k p − 1 2 ≡ 1 ( m o d p ) g^{k\frac{p-1}{2}}\equiv 1(\mod p) gk2p−1≡1(modp),由于原根一定不为二次剩余,所以 p − 1 ∣ k p − 1 2 p-1|k\frac{p-1}2 p−1∣k2p−1所以 k k k必然为偶数,那么 g k 2 g^{\frac k2} g2k即为 n n n的二次剩余。
那么我们便证明了 n p − 1 2 ≡ 1 ( m o d p ) n^{\frac{p-1}2}\equiv 1(\mod p) n2p−1≡1(modp)与 n n n为模 p p p意义下的二次剩余是等价的
因为 n p − 1 ≡ 1 ( m o d p ) n^{p-1}\equiv 1(\mod p) np−1≡1(modp)所以 n p − 1 2 n^{\frac{p-1}2} n2p−1为 1 1 1的二次剩余为 1 1 1或 − 1 -1 −1,所以若 n n n是二次剩余, n p − 1 2 n^{\frac{p-1}2} n2p−1的值为 − 1 -1 −1
Cipolla
我们现在考虑解方程 x 2 ≡ n x^2\equiv n x2≡n
首先找到一个 a a a满足 a 2 − n a^2-n a2−n是非二次剩余,期望是 2 2 2次即可找到
接下来定义 i 2 ≡ a 2 − n i^2\equiv a^2-n i2≡a2−n,这里的 i i i的定义类似于单位虚数的定义。
然后可以把所有数表示为 A + B i A+Bi A+Bi的形式。
那么我们可以有两个结论
引理1: i p ≡ − i i^p\equiv-i ip≡−i
证明: i p ≡ i ( i 2 ) p − 1 2 ≡ i ( a 2 − n ) p − 1 2 ≡ − i i^p\equiv i(i^2)^{\frac{p-1}2}\equiv i(a^2-n)^{\frac{p-1}2}\equiv-i ip≡i(i2)2p−1≡i(a2−n)2p−1≡−i
引理2: ( A + B ) p ≡ A p + B p (A+B)^p\equiv A^p+B^p (A+B)p≡Ap+Bp
证明:由二项式定理,由于 p p p为质数,除了 C p 0 C_p^0 Cp0, C p p C_p^p Cpp外的组合数分子上的阶乘没法消掉,模 p p p为 0 0 0,只剩 C p 0 A p + C p p B p C_p^0A^p+C_p^pB^p Cp0Ap+CppBp
最后我们就可以得出结论 ( a + i ) p + 1 ≡ n (a+i)^{p+1}\equiv n (a+i)p+1≡n
证明:
(
a
+
i
)
p
+
1
≡
(
a
p
+
b
p
)
(
a
+
i
)
≡
(
a
−
i
)
(
a
+
i
)
≡
a
2
−
i
2
≡
n
(a+i)^{p+1}\equiv(a^p+b^p)(a+i)\equiv(a-i)(a+i)\equiv a^2-i^2\equiv n
(a+i)p+1≡(ap+bp)(a+i)≡(a−i)(a+i)≡a2−i2≡n
那么
±
(
a
+
1
)
p
+
1
2
\pm(a+1)^{\frac{p+1}2}
±(a+1)2p+1就是方程的解
code
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
struct Com{LL r,i;};
LL w;
Com mul(Com a,Com b,LL P){
Com c;
c.r=(a.r*b.r%P+a.i*b.i%P*w%P)%P;
c.i=(a.r*b.i%P+a.i*b.r%P)%P;
return c;
}
LL fpowr(LL a,LL b,LL P){LL ret=1;
for(;b;b>>=1,a=a*a%P)
if(b&1)ret=ret*a%P;
return ret;
}
Com fpowc(Com a,LL b,LL P){Com ret={1,0};
for(;b;b>>=1,a=mul(a,a,P))
if(b&1)ret=mul(ret,a,P);
return ret;
}
LL Euler(LL n,LL P){
return fpowr(n,P-1>>1,P);
}
LL solve(LL n,LL P){
n%=P;
LL a;do a=rand()%P;
while(Euler(w=(a*a%P-n+P)%P,P)!=P-1);
return fpowc({a,1},P+1>>1,P).r;
}
int main(){
srand(time(NULL));
int T;scanf("%d",&T);
while(T--){
LL n,P,a,b;
scanf("%lld%lld",&n,&P);
if(!n){puts("0");continue;}
if(Euler(n,P)==P-1){puts("Hola!");continue;}
else{
a=solve(n,P),b=P-a;
if(a>b)swap(a,b);
printf("%lld %lld\n",a,b);
}
}
return 0;
}