• 浅谈二次剩余


    浅谈二次剩余

    定义:对于正整数(p,n),若存在(x)使得(x^2 equiv n(mod p)).则称(n)是模(p)的二次剩余。(在本文中我们只考虑(p)为奇质数的情况)

    勒让德括号,欧拉判别准则

    下面引入勒让德括号,它可以简化讨论:

    [left(frac{n}{p} ight)=left{egin{array}{ll}1 & (p mid n ext { 且 } n ext { 是模 } p ext { 的二次剩余 }) \ -1 & (p mid n ext { 且 } n ext { 是模 } p ext { 的二次非剩余 }) \ 0 & (p mid n)end{array} ight. ]

    定理(欧拉判别准则): 当(p)为奇素数时,(left( frac{n}{p} ight) equiv n^{frac{p-1}{2}} (mod p))

    证明: (p|n)的情况是显然的,我们考虑(p mid n)时的情况。显然(n^{frac{p-1}{2}}=plusmn 1).若(n)是二次剩余,则(n^{frac{p-1}{2}}=(x^2)^{frac{p-1}{2}}=x^{p-1}=1). 若(n^{frac{p-1}{2}}=1),把(n)用模(p)下的原根(g)表示为(n=g^k),那么(g^{kfrac{p-1}{2}}=1).又因为(g^{p-1}=1),由原根的性质(见此处定理3.1)得到(p-1|kfrac{p-1}{2}),那么(k)必定为偶数,令(x=g^{frac{k}{2}})即可,说明(n)是二次剩余。也就是说 (n^{frac{p-1}{2}}=1)(n)是二次剩余的充要条件。反之-1的情况成立,上面的(-1)在模意义下其实就是(p-1)

    从这个定理我们已经得到二次剩余的一种求法,求出原根(g)后用BSGS求出(g^k=n)的解,那么(g^{frac{k}{2}})就是一个解。复杂度(O(sqrt{p}))

    Cipolla算法

    上面这个(O(sqrt{p}))的算法还不够优秀,我们用Cipolla算法来求解(x^2 equiv n(mod p))。算法流程如下:

    1. 不断随机(a),直到找到一个(a)使得(a^2-n)是非二次剩余(用欧拉判别准则判断)(我们之后会证明期望随机次数为2).
    2. 定义(omega^2=a^2-n).但是我们知道(a^2-n)是非二次剩余,那么我们可以类比复数域对无法开根的数(-1)定义(mathrm{i}^2=-1),我们把所有数表示为(p+qomega)的形式,运算规则类似复数。那么(x=plusmn (a+omega)^{frac{p+1}{2}}).

    引理1:(x^2 equiv n(mod p))有且仅有两组解,且满足(x_1+x_2 equiv n(mod 0))

    证明:假设对于(x_1 eq x_2)(x_1^2 equiv x_2^2),移项得((x_1-x_2)(x_1+x_2)equiv 0)。 这很类似实数的开根。又因为每个(n)都对应两个不同的(x_1,x_2),一共只有(p-1)个数,那么二次剩余的数量恰为(frac{p-1}{2}).那么随机到非二次剩余的概率为(frac{p-1}{2p} approx frac{1}{2}).期望次数为(sum_{i=0}frac{i}{2^i}=2-frac{N+2}{2^N}),趋近于2. 这样算法的复杂度就是(O(log p))

    引理2:$ omega^{p} equiv-omega$
    证明: (omega^{p} equiv omegaleft(omega^{2} ight)^{frac{p-1}{2}} equiv omegaleft(a^{2}-n ight)^{frac{p-1}{2}} equiv-omega)

    引理3:((A+B)^{p} equiv A^{p}+B^{p})
    证明:根据二项式定理,由于(p)是质数,除了(C_{p}^{0}, C_{p}^{p}) 外的組合数分子上的阶乘没法消掉(p),模(p)都为(0,) 只有(C_{p}^{0} A^{0} B^{p}+C_{p}^{p} A^{p} B^{0})

    根据引理2,3:

    [(a+i)^{p+1} equivleft(a^{p}+i^{p} ight)(a+i)equivleft(acdot a^{p-1}+i^{p} ight)(a+i) equiv(a-i)(a+i) equiv a^{2}-i^{2} equiv n ]

    那么(x=plusmn (a+i)^{frac{p+1}{2}})(可以用反证法证明虚部为0,这里不再赘述)

    struct com { //cipolla用的类似复数的东西
        ll real;
        ll imag;//a+b*sqrt(w)
        com() {
    
        }
        com(ll _real,ll _imag) {
            real=_real;
            imag=_imag;
        }
    };
    inline com mul(com a,com b,ll w) {//重载乘法
        return com((a.real*b.real%mod+a.imag*b.imag%mod*w%mod)%mod,(a.real*b.imag%mod+a.imag*b.real%mod)%mod);
    }
    inline com fpow(com x,ll k,ll w) {
        com ans=com(1,0);
        while(k) {
            if(k&1) ans=mul(ans,x,w);
            x=mul(x,x,w);
            k>>=1;
        }
        return ans;
    }
    inline ll cipolla(ll x) {
        if(fast_pow(x,(mod-1)/2)==mod-1) return -1;//x不存在二次剩余
        while(1) {
            ll a=((rand()<<15)|rand())%mod;
            ll w=(a*a%mod-x+mod)%mod;
            if(fast_pow(w,(mod-1)/2)==mod-1) { //x-a^2不存在二次剩余
                return fpow(com(a,1),(mod+1)/2,w).real%mod;
            }
        }
        return -1;
    }
    

    完整代码:LuoguP5491

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    using namespace std;
    typedef long long ll;
    
    int T;
    ll n,mod;
    inline ll fast_pow(ll x,ll k) {
        ll ans=1;
        while(k) {
            if(k&1) ans=ans*x%mod;
            x=x*x%mod;
            k>>=1;
        }
        return ans;
    }
    inline ll inv(ll x) {
        return fast_pow(x,mod-2);
    }
    
    struct com { //cipolla用的类似复数的东西
        ll real;
        ll imag;//a+b*sqrt(w)
        com() {
    
        }
        com(ll _real,ll _imag) {
            real=_real;
            imag=_imag;
        }
    };
    inline com mul(com a,com b,ll w) {
        return com((a.real*b.real%mod+a.imag*b.imag%mod*w%mod)%mod,(a.real*b.imag%mod+a.imag*b.real%mod)%mod);
    }
    inline com fpow(com x,ll k,ll w) {
        com ans=com(1,0);
        while(k) {
            if(k&1) ans=mul(ans,x,w);
            x=mul(x,x,w);
            k>>=1;
        }
        return ans;
    }
    inline ll cipolla(ll x) {
        if(fast_pow(x,(mod-1)/2)==mod-1) return -1;//x不存在二次剩余
        while(1) {
            ll a=((rand()<<15)|rand())%mod;
            ll w=(a*a%mod-x+mod)%mod;
            if(fast_pow(w,(mod-1)/2)==mod-1) { //x-a^2不存在二次剩余
                return fpow(com(a,1),(mod+1)/2,w).real%mod;
            }
        }
        return -1;
    }
    
    int main() {
        scanf("%d",&T);
        while(T--){
            scanf("%lld %lld",&n,&mod);
            if(n==0){
                puts("0");
                continue;
            }
            ll ans=cipolla(n);
            if(ans==-1) puts("Hola!");
            else{
                ans=(ans%mod+mod)%mod;
                printf("%lld %lld
    ",min(ans,mod-ans),max(ans,mod-ans));
            }
        }
    }
    
  • 相关阅读:
    python入门 类的继承和聚合(五)
    如何快速找到多个字典中的公共键(1.4)
    python输入输出(二)
    python入门 集合(四)
    LOJ 3093: 洛谷 P5323: 「BJOI2019」光线
    LOJ 3049: 洛谷 P5284: 「十二省联考 2019」字符串问题
    【比赛游记】FJOI2019瞎打记
    ICPC World Finals 2019 题解
    LOJ 3043: 洛谷 P5280: 「ZJOI2019」线段树
    LOJ 2483: 洛谷 P4655: 「CEOI2017」Building Bridges
  • 原文地址:https://www.cnblogs.com/birchtree/p/13389893.html
Copyright © 2020-2023  润新知