浅谈二次剩余

二次剩余是数论基本概念之一。它是初等数论中非常重要的结果。俗称模意义开根。

二次剩余定义:若存在整数 x x x,对于整数 d d d 满足 x 2 ≡ a ( m o d p ) x^2\equiv a\pmod{p} x2a(modp),称 a a a 是模 p p p 意义下的二次剩余。

下面探讨 p p p 为奇素数的情况(因为 p = 2 p=2 p=2 时没什么意义)。使用 Cipolla \text{Cipolla} Cipolla 算法求解。

a = 0 a=0 a=0 时显然 x = 0 x=0 x=0,下面就不讨论了。

关于根如二次剩余的数量

x 1 ≠ x 2 x_1\not=x_2 x1=x2,且 x 1 , x 2 x_1,x_2 x1,x2 都是方程的根。

则有 x 1 2 − x 2 2 ≡ 0 ( m o d p ) x_1^2-x_2^2\equiv0\pmod p x12x220(modp)

变形得到 ( x 1 + x 2 ) ( x 1 − x 2 ) ≡ 0 ( m o d p ) (x_1+x_2)(x_1-x_2)\equiv0\pmod p (x1+x2)(x1x2)0(modp)

x 1 − x 2 x_1-x_2 x1x2 自然不等于零,所以有 x 1 + x 2 = 0 x_1+x_2=0 x1+x2=0

据此,容易证得方程的不同根最多只有两个。

意思是 x 2 ≡ d ( m o d p ) x^2\equiv d\pmod p x2d(modp) 的根只有两个,且互为相反数。

一个二次剩余对应两个不同的根,还是互为相反数。在模意义下可以找到 p − 1 2 \dfrac{p-1}{2} 2p1 对相反数,则 p p p p − 1 2 \dfrac{p-1}{2} 2p1 个二次剩余,自然 p p p p − 1 2 \dfrac{p-1}{2} 2p1 个非二次剩余。

勒让德符号和欧拉准则

勒让德符号定义如下
( a p ) = { 1 a 是 p 的二次剩余 − 1 a 是 p 的非二次剩余 0 a ≡ 0 ( m o d p ) \left(\dfrac{a}{p}\right)= \begin{cases}1&\text{a 是 p 的二次剩余}\\ -1&\text{a 是 p 的非二次剩余}\\ 0&a\equiv0\pmod p\\ \end{cases} (pa)= 110 p 的二次剩余 p 的非二次剩余a0(modp)
欧拉准则: ( a p ) = a p − 1 2 \left(\dfrac{a}{p}\right)=a^{\frac{p-1}2} (pa)=a2p1
证明:
由费马小定理得 a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv1\pmod p ap11(modp)

式子变换得到 ( a p − 1 2 + 1 ) ( a p − 1 2 − 1 ) ≡ 0 ( m o d p ) (a^{\frac{p-1}2}+1)(a^{\frac{p-1}2}-1)\equiv0\pmod p (a2p1+1)(a2p11)0(modp)

所以 a p − 1 2 a^{\frac{p-1}2} a2p1 等于 1 1 1 − 1 -1 1

提出命题:「 a a a p p p 的二次剩余」是「 a p − 1 2 ≡ 1 ( m o d p ) a^{\frac{p-1}2}\equiv1\pmod p a2p11(modp)」的充要条件。

  • 充分性:存在 x x x 使 x 2 ≡ a ( m o d p ) x^{2}\equiv a\pmod p x2a(modp),可得 x 2 ⋅ p − 1 2 ≡ a p − 1 2 ≡ 1 ( m o d p ) x^{2\cdot\frac{p-1}2}\equiv a^{\frac{p-1}2}\equiv1\pmod{p} x22p1a2p11(modp),得证。
  • 必要性:设 g g g p p p 的原根,则存在一个 k k k,有 a = g k a=g^{k} a=gk
    可以得到
    a p − 1 2 ≡ 1 ≡ g ( p − 1 ) ⋅ k 2 a^{\frac{p-1}2}\equiv1\equiv g^{(p-1)\cdot\frac{k}2} a2p11g(p1)2k
    由原根的性质可得 ( p − 1 ) k 2 (p-1)\dfrac k2 (p1)2k p − 1 p-1 p1 的倍数,所以 k k k 为偶数。因为 g k = a g^k=a gk=a,令 x = g k 2 x=g^{\frac k2} x=g2k,则 x 2 ≡ a ( m o d p ) x^2\equiv a\pmod p x2a(modp)。得证。

因为「 a a a p p p 的二次剩余」是「 a p − 1 2 ≡ 1 ( m o d p ) a^{\frac{p-1}2}\equiv1\pmod p a2p11(modp)」的充要条件,自然 「 a a a p p p 的二次非剩余」是「 a p − 1 2 ≡ − 1 ( m o d p ) a^{\frac{p-1}2}\equiv-1\pmod p a2p11(modp)」的充要条件,而若 a ≡ 0 ( m o d p ) a\equiv0\pmod p a0(modp),对应勒让德符号为 0 0 0。综上,欧拉准则得证。

Cipolla 算法

p p p 为奇素数,求二次剩余可用 Cipolla \text{Cipolla} Cipolla 算法。

算法流程:找出一个 x x x,使 x 2 − a x^2-a x2a p p p 的二次非剩余。然后令 i 2 ≡ x 2 − a ( m o d p ) i^2\equiv x^2-a\pmod p i2x2a(modp),则 ( x + i ) p + 1 2 (x+i)^{\frac{p+1}2} (x+i)2p+1 为方程的一个根,其相反数为方程的另一个根。

???
x 2 − a x^2-a x2a 不是二次非剩余呢,怎么会存在一个 i i i 使 i 2 i^2 i2 等于它呢?

类似于复数,这里的 i i i 是一个“虚数单位”。我们对实数进行数域扩充,所有数都能表示成 a + b i a+bi a+bi 的形式,其中 a , b a,b a,b 为实数。

下面证明 Cipolla \text{Cipolla} Cipolla 算法的正确性:

由二项式定理可得:
( x + i ) p ≡ ∑ j = 0 p C p j x j i p − j ( m o d p ) ≡ ∑ j = 0 p p ! j ! ( p − j ) ! x j i p − j ( m o d p ) \begin{aligned} (x+i)^{p}&\equiv\sum\limits_{j=0}^{p}C_p^jx^ji^{p-j}\pmod p\\ &\equiv\sum\limits_{j=0}^p\dfrac{p!}{j!(p-j)!}x^ji^{p-j}\pmod p \end{aligned} (x+i)pj=0pCpjxjipj(modp)j=0pj!(pj)!p!xjipj(modp)
观察 C p j C_p^j Cpj 发现只有 j j j 0 0 0 p p p 时等于 1 1 1,取其他值时模 p p p 0 0 0

( x + i ) p ≡ x p + i p ( m o d p ) (x+i)^{p}\equiv x^p+i^p\pmod p (x+i)pxp+ip(modp)
那么
( x + i ) p + 1 ≡ ( x + i ) ( x + i ) p ( m o d p ) ≡ ( x + i ) ( x p + i p ) ( m o d p ) \begin{aligned} (x+i)^{p+1}&\equiv(x+i)(x+i)^p\pmod p\\ &\equiv(x+i)(x^p+i^p)\pmod p \end{aligned} (x+i)p+1(x+i)(x+i)p(modp)(x+i)(xp+ip)(modp)
根据费马小定理, x p ≡ x ( m o d p ) x^p\equiv x\pmod p xpx(modp)
i p i^p ip 进行变形
i p ≡ i 2 ⋅ p − 1 2 ⋅ i ≡ ( x 2 − a ) p − 1 2 i ( m o d p ) i^p\equiv i^{2\cdot\frac{p-1}2}\cdot i\equiv(x^2-a)^{\frac{p-1}2}i\pmod p ipi22p1i(x2a)2p1i(modp)
由于 x 2 − a x^2-a x2a p p p 的非二次剩余,所以 ( x 2 − a ) p − 1 2 ≡ − 1 ( m o d p ) (x^2-a)^{\frac{p-1}2}\equiv-1\pmod p (x2a)2p11(modp)

可得 i p ≡ − i ( m o d p ) i^p\equiv-i\pmod p ipi(modp)


( x + i ) p + 1 ≡ x 2 − i 2 ≡ a ( m o d p ) (x+i)^{p+1}\equiv x^2-i^2\equiv a\pmod p (x+i)p+1x2i2a(modp)

所以 ( x + i ) p + 1 2 (x+i)^{\frac{p+1}2} (x+i)2p+1 是方程的根。

分析时间复杂度,因为质数 p p p p − 1 2 \dfrac{p-1}{2} 2p1 个非二次剩余,所以找 x x x 期望 2 2 2 次找到。后面用了一次快速幂求答案。总的时间复杂度为 O ( log ⁡ p ) O(\log p) O(logp)

代码

在实现中可以使用类似于复数的方式求 ( x + i ) p + 1 2 (x+i)^{\frac{p+1}2} (x+i)2p+1

定义一个“复数”类型 special complex \text{special complex} special complex

对于两个“复数” x 1 + y 1 i , x 2 + y 2 i x_1+y_1i,x_2+y_2i x1+y1i,x2+y2i,二者的乘积为 x 1 x 2 + y 1 y 2 i 2 + ( x 1 y 2 + y 1 x 2 ) i x_1x_2+y_1y_2i^2+(x_1y_2+y_1x_2)i x1x2+y1y2i2+(x1y2+y1x2)i

因为 i 2 ≡ x 2 − a i^2\equiv x^2-a i2x2a 的,这样就得出“复数”乘法的运算规则了。

具体实现如下

#include
using namespace std;
typedef long long ll;
ll n,mod,sqri;
struct special_complex
{
    ll a,b;
    special_complex(){}
    special_complex(ll x,ll y){a=x,b=y;}
    special_complex operator*(const special_complex &x){
        return special_complex((a*x.a+b*x.b%mod*sqri)%mod,(a*x.b+b*x.a)%mod);
    }
};
special_complex ksm(special_complex a,ll b)
{
    special_complex ans=special_complex(1,0);
    while(b){
        if(b&1) ans=ans*a;
        b>>=1;
        a=a*a;
    }
    return ans;
}
ll ksm(ll a,ll b)
{
    ll ans=1;
    while(b){
        if(b&1) ans=ans*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return ans;
}
ll get_quadratic_residue(ll n,ll mod)
{
    if(ksm(n,mod>>1)==mod-1) return -1;
    ll t=1ll*rand()*rand()%mod;
    while(ksm((t*t-n)%mod+mod,mod>>1)==1) t=1ll*rand()*rand()%mod;
    sqri=((t*t-n)%mod+mod)%mod;
    return ksm(special_complex(t,1),mod+1>>1).a;
}
int main()
{
    int t;
    cin>>t;
    while(t--){
        scanf("%lld%lld",&n,&mod);
        if(!n){puts("0");continue;}
        ll rt=get_quadratic_residue(n,mod);
        if(rt==-1){puts("Hola!");continue;}
        rt=min(rt,mod-rt);
        printf("%lld %lld\n",rt,mod-rt);
    }
}

你可能感兴趣的:(算法)