二次剩余小记
看 \(\text{yyb}\) 的博客看到的,发现似乎并没有想象中的那么难,就学了一下,过了板题,这里记录一下,暂时还是只会二次剩余, \(n\) 次剩余暂时先放一下。
模数为奇素数
下文的 \(p\) 即是模数。
我们称 \(n\) 为模 \(p\) 意义下的二次剩余当且仅当存在 \(x\) 使得 \(x^2\equiv n\pmod p,x\in \mathbb{N}\) 。下文的 \(\mathbb{N}\) 其实是自然数集。
- 引理1
\((A+B)^p\equiv A^p+B^p\pmod{p}\)
- 证明
显然只需要说明 \(\dbinom{p}{i}\equiv0\pmod p,s.t.\ \ 1\le i\le p-1\),然后我们发现如果 \(p\) 是个奇素数的话,那么分子里面的 \(p\) 就没有办法消去,于是就得证了。
- 引理2
\(n^{\frac{p-1}{2}}\equiv 1\pmod p\)
是 \(n\) 是模 \(p\) 意义下的二次剩余的必要条件。
- 证明
必要性 :设 \(x^2\equiv n\pmod p\),那么有 \(x^{p-1}\equiv 1\pmod p\) ,证毕。
充分性 :显然。
接下来,我们考虑随机出一个 \(a\) 使得 \(a^2-n\) 不是一个二次剩余,这样的期望尝试次数大概为 \(2\),再设 \(i^2=a^2-n\) ,这里的 \(i\) 显然就不属于 \(\mathbb{N}\) ,我们把它视作虚数之类的东西就好了。然后我们发现就有个性质:
- 性质
证明 :首先我们知道 \((i^2)^{\frac{p-1}{2}}\equiv (a^2-n)^{\frac{p-1}{2}}\not\equiv 1\pmod p\) (因为 \(a^2-n\) 不是一个二次剩余),然后又有 \((i^2)^{p-1}\equiv 1\pmod p\),所以 \(i^{p-1}\equiv -1\pmod p\)。
我们可(bu)以(neng)发现:
就是说 \((a+i)^{p+1}\equiv n\pmod p\),所以说 \(\sqrt n\equiv (a+i)^{\frac{p+1}{2}}\pmod p\)。
可能有人有疑问,\((a+i)^{\frac{p+1}{2}}\) 有没有可能不是一个整数,显然如果你保证有解的话它显然就是一个整数。
\(\texttt{Code}\)
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define int long long
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
int p,sqri;
int qkpow (int a,int b){
int res = 1;for (;b;b >>= 1,a = 1ll * a * a % p) if (b & 1) res = 1ll * res * a % p;
return res;
}
int mul (int a,int b){return 1ll * a * b % p;}
int dec (int a,int b){return a >= b ? a - b : a + p - b;}
int add (int a,int b){return a + b >= p ? a + b - p : a + b;}
struct node{
int real,imag;
node (int _real,int _imag) : real (_real) , imag (_imag) {}
node operator * (node b){return node (add (1ll * real * b.real % p,1ll * imag * b.imag % p * sqri % p),add (1ll * real * b.imag % p,1ll * b.real * imag % p));}
node operator + (node b){return node (add (real,b.real),add (imag,b.imag));}
node operator ^ (int b){node a = *this,res = node (1,0);for (;b;b >>= 1,a = a * a) if (b & 1) res = res * a;return res;}
};
bool check_if_qs (int n){//判断是否是一个二次剩余
return qkpow (n,(p - 1) >> 1) == 1;
}
bool tryit (int n){
if (n == 0){
puts ("0");
return 1;
}
int a = rand ();sqri = dec (mul (a,a),n);
if (!a || !check_if_qs (sqri)){
int x0 = (node (a,1) ^ (p + 1 >> 1)).real;
int x1 = p - x0;if (x0 > x1) swap (x0,x1);
write (x0),putchar (' '),write (x1),putchar ('\n');
return 1;
}
else return 0;
}
signed main(){
int T;read (T);
while (T --> 0){
int n;read (n,p);
if (!check_if_qs (n) && n) puts ("Hola!");
else while (1) if (tryit (n)) break;
}
return 0;
}