二次剩余与Cipolla算法
若存在x,使得
x
2
≡
a
(
m
o
d
p
)
x^2\equiv a \pmod{p}
x2≡a(modp)
成立且a不是p的倍数,则称a为模p的二次剩余。
下文只讨论p为奇素数的情况。
1、模p二次剩余的个数
对于一个素数p,p的二次剩余有(p-1)/2个,非二次剩余有(p-1)/2个。
因为有
(
p
−
x
)
2
≡
x
2
(
m
o
d
p
)
(p-x)^2\equiv x^2 \pmod{p}
(p−x)2≡x2(modp),所以在[1,(p-1)/2]范围内即可得到模p的所有二次剩余数,只需要证明在[1,(p-1)/2]范围内x2 两两不相等即可。
假设存在
x
,
y
∈
[
1
,
p
−
1
2
]
x,y \in[1,\frac{p-1}{2}]
x,y∈[1,2p−1],有
x
2
≡
y
2
(
m
o
d
p
)
x^2\equiv y^2\pmod{p}
x2≡y2(modp),则有
x
2
−
y
2
≡
0
(
m
o
d
p
)
(
x
+
y
)
(
x
−
y
)
≡
0
(
m
o
d
p
)
x^2-y^2\equiv 0\pmod{p}\newline (x+y)(x-y)\equiv 0\pmod{p}
x2−y2≡0(modp)(x+y)(x−y)≡0(modp)
根据x,y的取值范围,显然
1
<
x
+
y
<
p
1<x+y<p
1<x+y<p,又因为
x
≠
y
且
−
p
<
x
−
y
<
p
x\ne y且-p<x-y<p
x=y且−p<x−y<p,所以上述等式不成立。
2、勒让德符号
勒让德符号,或二次特征,是一个由阿德里安-马里·勒让德在1798年尝试证明二次互反律时引入的函数。具体形式为
(
n
p
)
=
{
1
,
n
为
p
的
二
次
剩
余
−
1
,
n
不
为
p
的
二
次
剩
余
0
,
n
为
p
的
倍
数
\left (\frac{n}{p}\right)= \left\{\begin{matrix} 1,n为p的二次剩余\\ -1,n不为p的二次剩余\\ 0,n为p的倍数 \end{matrix}\right.
(pn)=⎩⎨⎧1,n为p的二次剩余−1,n不为p的二次剩余0,n为p的倍数
3、欧拉判别准测
(
n
p
)
≡
n
p
−
1
2
(
m
o
d
p
)
\left (\frac{n}{p} \right )\equiv n^{\frac{p-1}{2}}\pmod{p}
(pn)≡n2p−1(modp)
n为p的二次剩余当且仅当
n
p
−
1
2
≡
1
(
m
o
d
p
)
n^{\frac{p-1}{2}}\equiv 1\pmod{p}
n2p−1≡1(modp)
n为p的非二次剩余当且仅当
n
p
−
1
2
≡
−
1
(
m
o
d
p
)
n^{\frac{p-1}{2}}\equiv -1\pmod{p}
n2p−1≡−1(modp)
证明(基于费马小定理):
x
p
−
1
≡
1
(
m
o
d
p
)
(
x
p
−
1
2
−
1
)
(
x
p
−
1
2
+
1
)
≡
1
(
m
o
d
p
)
x^{p-1}\equiv 1\pmod{p}\newline (x^{\frac{p-1}{2}}-1)(x^{\frac{p-1}{2}}+1)\equiv1\pmod{p}\newline
xp−1≡1(modp)(x2p−1−1)(x2p−1+1)≡1(modp)
所以有
x
p
−
1
2
≡
1
(
m
o
d
p
)
x^{\frac{p-1}{2}}\equiv 1 \pmod{p}
x2p−1≡1(modp)或
x
p
−
1
2
≡
−
1
(
m
o
d
p
)
x^{\frac{p-1}{2}}\equiv -1 \pmod{p}
x2p−1≡−1(modp),显然两者不能同时成立。
当有
x
p
−
1
2
≡
1
(
m
o
d
p
)
x^{\frac{p-1}{2}}\equiv 1 \pmod{p}
x2p−1≡1(modp)成立时:
由于p是素数,所以p一定存在一个原根g,使得
g
t
≡
x
(
m
o
d
p
)
g^t\equiv x\pmod{p}
gt≡x(modp)。
故有
g
t
∗
p
−
1
2
≡
1
(
m
o
d
p
)
g^{t*\frac{p-1}{2}}\equiv 1 \pmod{p}
gt∗2p−1≡1(modp),又因为
o
r
d
p
(
g
)
=
p
−
1
ord_p(g)=p-1
ordp(g)=p−1,所以有
(
p
−
1
)
∣
(
t
∗
p
−
1
2
)
(p-1)|({t*\frac{p-1}{2}})
(p−1)∣(t∗2p−1)。
故
t
2
>
=
1
且
2
∣
t
\frac{t}{2}>=1且2|t
2t>=1且2∣t,令i=t/2,则
g
t
≡
g
2
∗
i
≡
x
(
m
o
d
p
)
g^t\equiv g^{2*i}\equiv x\pmod{p}
gt≡g2∗i≡x(modp)
证毕。
4、Cipolla算法
该算法可以解决形如
x
2
≡
n
(
m
o
d
p
)
x^2\equiv n \pmod{p}
x2≡n(modp)之类的方程。
算法流程:
(1)、首先找到一个数a,使得a2-n为模p的一个非二次剩余,即
(
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)。因为模p的非二次剩余与二次剩余均有(p-1)/2个,所以用随机取值的方法根据欧拉判别准则可以很快的找出符合条件的数a。
(2)、构建一个复数域,令
i
2
=
a
2
−
n
i^2=a^2-n
i2=a2−n,那么有
i
p
−
1
≡
−
1
(
m
o
d
p
)
i^{p-1}\equiv -1 \pmod p
ip−1≡−1(modp)。那么方程的其中一个解为
(
a
+
i
)
p
+
1
2
(a+i)^{\frac{p+1}{2}}
(a+i)2p+1,此过程可以用快速幂去优化。
证明:
(
a
+
i
)
p
+
1
2
≡
a
p
+
1
2
+
i
p
+
1
2
≡
(
a
p
+
1
+
i
p
+
1
)
1
2
≡
(
a
2
+
i
2
)
1
2
≡
n
1
2
\begin{aligned}(a+i)^{\frac{p+1}{2}}\ &\equiv a^{\frac{p+1}{2}}+i^{\frac{p+1}{2}}\\ &\equiv(a^{p+1}+i^{p+1})^{\frac{1}{2}}\\&\equiv (a^2+i^2)^{\frac{1}{2}}\\ &\equiv n^{\frac{1}{2}} \end{aligned}
(a+i)2p+1 ≡a2p+1+i2p+1≡(ap+1+ip+1)21≡(a2+i2)21≡n21
所以有
x
≡
(
a
+
i
)
p
+
1
2
≡
n
1
2
(
m
o
d
p
)
x\equiv (a+i)^{\frac{p+1}{2}}\equiv n^{\frac{1}{2}}\pmod p
x≡(a+i)2p+1≡n21(modp)
另外一解即为 x ′ = p − x x'=p-x x′=p−x
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct Complex{
ll a;
ll b;
Complex(ll x,ll y):a(x),b(y){}
};
ll w;
Complex complex_mul(Complex x,Complex y,ll p)
{
Complex ans(0,0);
ans.a=((x.a*y.a%p+x.b*y.b%p*w%p)%p+p)%p;
ans.b=((x.a*y.b%p+x.b*y.a%p)%p+p)%p;
return ans;
}
ll qpow_real(ll a,ll n,ll p)
{
ll ans=1;
while(n)
{
if(n&1)ans=ans*a%p;
a=a*a%p;
n>>=1;
}
return ans;
}
ll qpow_imag(Complex b,ll n,ll p)
{
Complex ans(1,0);
while(n)
{
if(n&1)ans=complex_mul(ans,b,p);
b=complex_mul(b,b,p);
n>>=1;
}
return ans.a%p;
}
ll Cipolla(ll n,ll p)
{
n%=p;
if(p==2)return n;
if(qpow_real(n,(p-1)/2,p)==p-1)return -1;
ll a;
while(1)
{
a=rand()%p;
w=((a*a%p-n)%p+p)%p;
if(qpow_real(w,(p-1)/2,p)==p-1)break;
}
Complex l(a,1);
return qpow_imag(l,(p+1)/2,p);
}
int main(void)
{
int t;
srand(time(NULL));
ll n,p;
scanf("%d",&t);
while(t--)
{
scanf("%lld%lld",&n,&p);
if(n==0){
printf("0\n");
continue;
}
ll ans=Cipolla(n,p);
if(ans==-1)printf("Hola!\n");
else{
ll t=(p-ans)%p;
if(t==ans)printf("%lld\n",ans);
else {
if(t>ans)printf("%lld %lld\n",ans,t);
else printf("%lld %lld\n",t,ans);
}
}
}
return 0;
}
1002



被折叠的 条评论
为什么被折叠?



