1. 程式人生 > 實用技巧 >【數論】BSGS

【數論】BSGS

BSGS(baby-step giant-step)

學習資料:OI Wiki

基礎篇

BSGS (baby-step giant step),常用於求解離散對數問題,該演算法可以在 \(\mathcal{O(\sqrt p)}\) 的時間內求解

\[a^x \equiv b\quad (mod\;p) \]

其中 \(a\,\bot\,p\) 。方程的解 \(x\) 滿足 \(0\le x<p\)

演算法描述

\(x=A[\sqrt p]-B\),其中 \(0\le A,B\le \lceil \sqrt p\rceil\) ,則有

\[a^{A\lceil\sqrt p\rceil -B}\equiv b\;(mod\,p) \]

稍加變換,則有

\[a^{A\lceil\sqrt p\rceil}\equiv ba^B\;(mod\,p) \]

我們已知的是 \(a,b\),所以我們可以先算出等式右邊的 \(ba^B\) 的所有取值,列舉 \(B\),用 hash / map 存下來,然後逐一計算 \(a^{A\lceil\sqrt p\rceil}\) ,列舉 \(A\) ,尋找是否有與之相等的 \(ba^B\) ,從而我們可以得到所有的 \(x\)\(x=A\lceil\sqrt p\rceil-B\)

注意到 \(A,B\) 均小於 \(\lceil\sqrt p\rceil\),所以時間複雜度為 \(\mathcal{Q(\sqrt p)}\)

,用 map 則多用一個 \(log\)

擴充套件篇

接下來我們求解

\[a^x\equiv b\quad(mod\;p) \]

其中 \(a,p\) 不一定互質。

具體地,設 \(d_1=gcd(a,p)\) 。如果 \(d_1\nmid b\) ,則原方程誤解。否則我們把方程除以 \(d_1\) ,得到

\[\frac{a}{d_1}\cdot a^{x-1}\equiv\frac{b}{d_1}\quad(mod\;\frac{p}{d_1}) \]

如果 \(a\)\(\frac{p}{d_1}\) 仍不互質就再除,設 \(d_2=gcd(a,\frac{p}{d_1})\) 。如果 \(d_2\nmid \frac{p}{d_1}\)

,則方程無解;否則同時除以 \(d_2\) 得到

\[\frac{a^2}{d_1d_2}\cdot a^{x-2}\equiv\frac{b}{d_1d_2}\quad(mod\;p) \]

同理,這樣不停判斷下去直到 \(a\bot\frac{p}{d_1d_2\cdots d_k}\)

\(D=\prod^k_{i=1}d_i\) ,於是方程就變成了這樣:

\[\frac{a^k}D\cdot a^{x-k}\equiv\frac bD\quad(mod\;\frac pD) \]

由於 \(a\bot \frac pD\) ,於是推出 \(\frac{a^k}D\bot\frac pD\) 。這樣 \(\frac{a^k}D\) 就有逆元了,於是把它丟到方程右邊,這就是一個普通的 BSGS 問題了,於是求解 \(x-k\) 後再加上 \(k\) 就是原方程的解。

注意,不排除解小於等於 \(k\) 的情況,所以在消因子之前做一下 \(\mathcal{O(k)}\) 列舉,直接驗證 \(a^i\equiv b\;(mod\,p)\) ,這樣就能避免這種情況。

BSGS && exBSGS 模板

luoguP4195

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

ll kpow(ll a,int b,int p){
    ll ans=1;
    while(b){
        if(b&1)ans=ans*a%p;
        a=a*a%p;
        b>>=1;
    }
    return ans;
}
ll exgcd(ll a,ll b,ll &x,ll &y){
    if(!b){x=1;y=0;return a;}
    ll g=exgcd(b,a%b,x,y);
    ll t=x;x=y;
    y=t-a/b*y;
    return g;
}
ll inv(ll a,ll p)//exgcd求逆元, 前提 gcd(a,p)==1
{
    ll x,y;
    exgcd(a,p,x,y);
    x=(x+p)%p;
    if(!x)x+=p;
    return x;
}
map<int,int>mp;
int BSGS(ll a,ll b,ll p)// gcd(a,p)==1
{
    a%=p;b%=p;
    int sp=ceil(sqrt(p));
    ll pa=b,ap=kpow(a,sp,p);mp.clear();
    for(int i=0;i<sp;i++,pa=pa*a%p)mp[pa]=i;
    pa=1;
    for(int i=0,j=0;i<=sp;i++,pa=pa*ap%p,j+=sp)
        if(mp.count(pa)&&j-mp[pa]>=0)return j-mp[pa];
    return -1;
}
int exBSGS(ll a,ll b,ll p)
{
    a%=p;b%=p;
    int k=0,t;ll tp=p,tb=b,ta=1;
    while((t=__gcd(a,tp))!=1){
        if(tb%t)return -1;
        tp/=t,tb/=t,ta=ta*a/t%tp;k++;
    }
    for(int i=0;i<=k;i++)
        if(kpow(a,i,p)==b)return i;
    tb=tb*inv(ta,tp)%tp;
    return BSGS(a,tb,tp)+k;
}
int main()
{
    ll a,b,p;
    while(scanf("%lld%lld%lld",&a,&p,&b)&&(a||b||p))
    {
        int res=exBSGS(a,b,p);
        if(res==-1)puts("No Solution");
        else printf("%d\n",res);
    }
}