二次剩余学习笔记
二次剩余, 俗称模意义开根。
实际上是求 \(x^2\equiv n \pmod p\) 的解。
这有两种解法,一个是利用原根来求,另外一种就是 \(Cipolla\) 算法(只能适用于奇素数的情况)。
我们下面主要介绍第二种。
1.勒让德符号
对于一个正整数 \(n\), 他的勒让德符号 \(\left(\dfrac{n} {p}\right)\)定义为:
根据这个我们可以初步判断一个数是否存在二次剩余。
2.解的数量
对于二次剩余 \(x^2\equiv n\pmod p\) 有多少个解?
假设有多组解,对于任意不相同的两个解有 \(x_0^2 = x_1^2\)
移项可得: \((x_0-x_1)(x_0+x_1) \equiv 0 \pmod p\)
因为 \(p\) 是奇素数,且 \(x_0 \neq x_1\), 所以 \(x_0-x_1 \neq 0\)
那么 \(x_0+x_1\equiv 0\), 也就是说 \(x_0\) 和 \(x_1\) 在模 \(p\) 的意义下互为相反数。
换而言之就是这个方程只有两个解,且这两个解在模 \(p\) 的意义下互为相反数。
另外,每一对相反数都可以对应一个二次剩余,而且这些二次剩余是两两不同的。
也就是说 二次剩余的数量恰为 \(p \over 2\) ,非二次剩余的数量也为 \({p\over 2}\)
3.欧拉判别
\(\left(\dfrac{n}{p} \right) = n^{{p-1}\over 2} \bmod p\)
4.Cipolla
求解二次剩余方程 \(x^2\equiv n \pmod p\)
我们先随机出一个 \(a\) 使得 \(a^2-n\) 不存在二次剩余, 由于非二次剩余的数量接近于 \(p\over 2\)。
所以期望随机两次就可以随机到一个合适的 \(a\) 。
然后设 \(w = \sqrt{a^2-n}\) , 但 \(a^2-n\) 不存在二次剩余,怎么把 \(w\) 求出来呢?
我们实际上不需要把这个 \(w\) 求出来。 类似于复数,定义一个 \(w\) 使得 \(w^2 = a^2-n\) 。
这样每个数都可以表示成: \(a+bw\) 的形式, \(a\) 和 \(b\) 都是模 \(p\) 意义下的数。
有一个结论: \((a+w) ^{p+1} = n\)
引理1: \((A+B)^p = A^p + B^p\)
证明:由二项式定理展开可得: \((A+B)^p = \displaystyle\sum_{i=1}^{p} C_{p}^{i} x^iy^{p-i}\)
在模 \(p\) 意义下,组合数 \(C_{p}^{i}\) 只有 \(C_{p}^{0},C_{p}^{p}\) 为 \(1\) ,其他的由于分子上的阶乘被消掉了值为 \(0\).
所以 \((A+B)^p = A^p + B^p\)
引理2: \(w^{p} = -w\)
证明: 因为 \(w^{p} = w(w^2)^{p-1\over 2} = w(a^2-n)^{p-1\over 2} = -w\)
我们来通过两个引理证一下上面那个结论
\((a+w)^{p+1} = (a+w) (a^{p} + w^{p}) = (a+w)(a-w) = a^2 - w^2\)
又因为: \(w^2 = a^2 - n\) , 所以: \(n = a^2-w^2\)
得证: \((a+w)^{p+1} = n\)
可以得到 \(x = (a+w)^{p+1\over 2}\), 又因为 \(p\) 为奇素数,所以 \(p+1\over2\) 肯定为整数,那么 \(x\) 也一定可以算出来。
对于 \((a+bw)(c+dw) = (ac + dbw^2) + (ad+bc)w\) ,重载一下乘法,在进行快速幂即可。
还有一个问题,\(x\) 虚部一定为 \(0\) 吗
我们来简单证明一下:
把 \(x\) 写成复数形式可得: \(x^2\equiv n \Rightarrow (a+bw)^2 \equiv n\)
展开可得: \(a^2 + 2abw + bw^2 \equiv n\)
移项可得: \(a^2+bw^2-n \equiv -2abw \Rightarrow a^2 + b(a^2-n) - n \equiv -2abw\)
因为左边虚部为 \(0\) ,所以右边的虚部肯定也为 \(0\), 即 \(ab = 0\) 。
假设说 \(b\neq 0\), 那么只能 \(a = 0\) 。
所以 \((bw)^2 \equiv n \Rightarrow w^2 = {n\over b^2}\),
由于 \(n\over b^2\) 在模 \(p\) 的意义下是确定的,所以 \(w^2\) 是二次剩余,这与前面所说的 \(w^2\) 不是二次剩余相矛盾。
所以 \(b = 0\), 即 \(x\) 的虚部肯定为 \(0\).
最后方程的两个解分别是 \(x\) 和 \(p-x\)
code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<bits/stdc++.h>
using namespace std;
#define int long long
int T,n,p,a,w;
struct Complex
{
int x,y;
Complex(){}
Complex(int a,int b){x = a, y = b;}
};
inline int read()
{
int s = 0,w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
return s * w;
}
Complex operator * (Complex a,Complex b)
{
Complex c;
c.x = (a.x * b.x % p + a.y * b.y % p * w % p) % p;
c.y = (a.x * b.y % p + a.y * b.x % p) % p;
return c;
}
Complex Pow(Complex a,int b)
{
Complex c = Complex(1,0);
for(; b; b >>= 1)
{
if(b & 1) c = c * a;
a = a * a;
}
return c;
}
int ksm(int a,int b)
{
int res = 1;
for(; b; b >>= 1)
{
if(b & 1) res = res * a % p;
a = a * a % p;
}
return res;
}
bool pd(int n)
{
return ksm(n,(p-1)/2) == 1;
}
void slove(int n,int p)
{
n %= p;
if(!pd(n))
{
printf("Hola!\n");
return;
}
srand(time(0)); rand();
while(1)
{
a = rand() % p;
w = (a * a % p - n + p) % p;
if(ksm(w,(p-1)/2) == p-1) break;
}
Complex Ans = Pow(Complex(a,1),(p+1)/2);
int ans1 = Ans.x, ans2 = p - Ans.x;
if(ans1 > ans2) swap(ans1,ans2);
printf("%lld ",ans1);
if(ans1 == ans2) printf("\n");
else printf("%lld\n",ans2);
}
signed main()
{
T = read();
while(T--)
{
n = read(); p = read();
if(n == 0) printf("%d\n",0);
else slove(n,p);
}
return 0;
}