高次同余方程

求解 \(a^x\equiv b(\mod p)\).

大步小步算法,\(BSGS(baby-step giant-step)\),要求 \(gcd(a,p)=1\),可在 \(O(\sqrt p)\) 时间复杂度内求解。

\(p<=10^{16}\) 时没有大问题。

方程的解满足 \(0<=x<p\).

\(t=\left \lceil \sqrt p \right \rceil,0<=j<t,x=i*t-j\),则 \(a^{i*t-j}\equiv b(\mod p)\).

两边同时乘上 \(a^j\) 可得,\(a^{i*t}\equiv ba^j(\mod p)\).

对于所有的 \(j\in [0,t-1]\),将 \(ba^j\) 插入 \(hash/map\) 中。

枚举所有的 \(i\in[0,t]\),查找表中是否存在 \(a^{i*t}\),若存在则可以得到对应的 \(j\)\(x=i*t-j\).

inline int BSGS(int a,int b,int p){
    a%=p;
    b%=p;
    if(b==1)return 0;
    mp.clear();
    int t=sqrt(p)+1,re=1;/*初值a^0*/
    for(int i=0;i<t;i++)mp[re*b%p]=i,(re*=a)%=p;/*依次插入b*a^i*/
    a=re;/*re=a^t%p*/
    re=1;
    for(int i=0;i<=t;i++){
        int j=mp.find(re)==mp.end()?-1:mp[re];
        if(j>=0&&i*t-j>=0)return i*t-j;
        (re*=a)%=p;
    }
    return -1;
}

扩展BSGS算法

求解 \(a^x\equiv b(\mod p)\)\(gcd(a,p)\not=1\).

将不互质的情况转化为互质的情况,设 \(g=gcd(a,p)\),两边同时除以 \(g\).

得到,\(\frac{a^x}{g}\equiv \frac{b}{g}(\mod \frac{p}{g})\),即 \(a^{x-1}\frac{a}{g}\equiv \frac{b}{g}(\mod \frac{p}{g})\).

\(g\not\mid b\)\(b\not=1\) 时无解。

一直递归直到 \(gcd(a,p)=1\),假设一共递归了 \(cnt\) 次,设 \(d=\prod_{i=1}^{k}g_i\).

原式即,\(a^{x-cnt}\frac{a^{cnt}}{d}\equiv \frac{b}{d}(\mod \frac{p}{g})\).

于是用 \(a,\frac{b}{g},\frac{p}{g}\)\(BSGS\) ,注意还有一个系数 \(\frac{a^{cnt}}{d}\) 要乘进去,出入 \(BSGS\) 中。

inline int BSGS(int a,int b,int p,int ad){
    a%=p,b%=p;
    mp.clear();
    int t=sqrt(p)+1,re=1;
    for(int i=0;i<t;i++)mp[re*b%p]=i,(re*=a)%=p;
    a=re;
    re=ad;
    for(int i=0;i<=t;i++){
        int j=mp.find(re)==mp.end()?-1:mp[re];
        if(j>=0&&i*t-j>=0)return i*t-j;
        (re*=a)%=p;
    }
    return -1;
}
inline int exBSGS(int a,int b,int p){
    a%=p;
    b%=p;
    if(b==1||p==1)return 0;/*特判*/
    int cnt=0,g=gcd(a,p),ad=1;
    while(g>1){
        if(b%g)return -1;/*判断无解*/
        cnt++;/*累加次数*/
        b/=g;
        p/=g;
        (ad*=a/g)%=p;/*更新系数*/
        if(ad==b)return cnt;/*ad=b则前面的一项a的指数为0,直接返回cnt*/
        g=gcd(a,p);
    }
    int ans=BSGS(a,b,p,ad);
    if(ans==-1)return -1;
    return ans+cnt;
}

也可以不将系数传进去,将 \(b\) 乘上系数的逆元即可,注意用 \(exgcd\) 计算。

inline int inv(int a,int p){
    int x,y;
    exgcd(a,p,x,y);
    return (x%p+p)%p;
}
inline int BSGS(int a,int b,int p){
    a%=p,b%=p;
    mp.clear();
    int t=sqrt(p)+1,re=1;
    for(int i=0;i<t;i++)mp[re*b%p]=i,(re*=a)%=p;
    a=re;
    re=1;
    for(int i=0;i<=t;i++){
        int j=mp.find(re)==mp.end()?-1:mp[re];
        if(j>=0&&i*t-j>=0)return i*t-j;
        (re*=a)%=p;
    }
    return -1;
}
inline int exBSGS(int a,int b,int p){
    a%=p;
    b%=p;
    if(b==1||p==1)return 0;
    int cnt=0,g=gcd(a,p),ad=1;
    while(g>1){
        if(b%g)return -1;
        cnt++;
        b/=g;
        p/=g;
        (ad*=a/g)%=p;
        if(ad==b)return cnt;
        g=gcd(a,p);
    }
    int ans=BSGS(a,b*inv(ad,p)%p,p);/*乘上系数的逆元*/
    if(ans==-1)return -1;
    return ans+cnt;
}
posted @ 2022-11-20 15:41  半步蒟蒻  阅读(95)  评论(0)    收藏  举报