二次剩余
Aleph_Drawer · · 算法·理论
0 前言
二次剩余,是一个并不高频的考点,但是它会在你意想不到的地方出现,就比如说一个月前的省选模拟的一道题目。
二次剩余通常是指求以下同余方程的两个解
容易发现,这与开根的计算类似,故又被称做模意义开根,这里给出了二次剩余的严格定义:
设
m 是正整数,a 是与m 互素的整数,若(a,m) = 1 ,且同余方程x^2 \equiv a \pmod m 有解,则称a 为m 的二次剩余。若同余方程x^2 \equiv a \pmod m 无解,则称a 为m 的二次非剩余。
1 勒让德符号
为了方便讨论,我们引入了勒让德符号,如果下面没有说明,我们保证
关于勒让德符号的定义如下(
(部分的参考资料上,还存在
2 欧拉准则及其证明
我们首先以一个命题开始。
尝试证明以下命题,其中
对于
根据费马小定理,有
即为
对于
对于
(\frac ap) = 1 的情况,ax \equiv b \pmod p (b 为任意整数),一定有\text{mod } p 的唯一解(换句话说,在[0,p) 的整数内有解)。
因为
由于
考虑将
将这两个组相乘起来得到:
根据威尔逊定理:
(p - 1) ! \equiv -1 \pmod p \leftrightarrow p \text { is a prime.}
得到
即为
这个定理被称作欧拉准则,或者说欧拉判别式。
2 求解 —— Cipolla 算法
对于同余方程
我们寻找整数
但同时我们定义
容易发现,对于
2.1 关于 Cipolla 算法的证明
2.1.1 关于正确性
下面我们来证明 Cipolla 算法的正确性,以及时间复杂度。
我们考虑分解
我们发现对于
所以原式可以化成
根据费马小定理,我们有
同时对于
根据欧拉准则,我们可以将其化为
所以将原式转化为
所以我们证明了
2.1.2 关于时间复杂度
前面说过,Cipolla 算法的时间复杂度是
考虑关于
这等价于
容易发现,对于
因此我们一共有
所以满足没有解的
所以在期望下,寻找
3 Cipolla 算法的代码
expand{a,b} 表示拓展之后的值域,代表 times(expand &a, expand b) 即拓展乘法中考虑到。
模板题:P5491,我真的在努力让代码变短了!
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll T, n, p, a;
struct expand {
ll a, b;
};
ll qpow(ll bas, ll exp) {
ll res = 1;
while(exp) {
if(exp & 1)
res *= bas,
res %= p;
bas *= bas;
bas %= p;
exp >>= 1;
}
return (res % p + p) % p;
}
void times(expand &x, expand y) {
expand c;
c.a = c.b = 0;
c.a = x.a * y.a % p + (a * a - n) * x.b % p * y.b % p;
c.a %= p;
c.b = x.a * y.b % p + x.b * y.a % p;
c.b %= p;
x = c;
return;
}
expand expand_qpow(expand bas, ll exp) {
expand res;
res.a = 1, res.b = 0;
while(exp) {
if(exp & 1)
times(res, bas);
times(bas, bas);
exp >>= 1;
}
res.a = (res.a % p + p) % p;
return res;
}
int main() {
scanf("%lld", &T);
for(; T; --T) {
scanf("%lld%lld", &n, &p);
if(qpow(n, (p - 1) / 2) != 1) {
printf("Hola!\n");
continue;
}
a = 0;
for(ll i = 0; i <= p; i++) {
if(qpow(i * i - n, (p - 1) >> 1) == p - 1) {
a = i;
break;
}
}
expand init;
init.a = a, init.b = 1;
expand x0 = expand_qpow(init, (p + 1) >> 1);
if(!x0.a)
printf("0\n");
else {
if(x0.a <= (-x0.a % p + p) % p)
printf("%lld %lld\n", x0.a, (-x0.a % p + p) % p);
else
printf("%lld %lld\n", (-x0.a % p + p) % p, x0.a);
}
}
return 0;
}
4 参考引用文献
- 《Elementary Number Theory and Its Applications》,第六版,Kenneth H.Rosen;
- Pecco大神的算法学习笔记(41): 二次剩余;
- 匿名用户回答的关于Cipolla性质的证明请问如何证明求二次剩余的经典cipolla算法中的一个简单性质?。