1. 程式人生 > 實用技巧 >BSGS 學習筆記

BSGS 學習筆記

問題:求$a^x\equiv b\ (mod\ p)$的最小正整數解。

這時候就要用到BSGS(拔山蓋世)演算法。直接進入正題:

設$x=im-n$,

則原式等於$a^{im-n}\equiv b\ (mod\ p)$。

移項,得$a^{im}\equiv a^nb(mod\ p)$。

我們把所有$a^nb$的狀態存到一個map裡,然後列舉$a^{im}$,如果相等則找到最小正整數解。

當$m=\sqrt p$時,演算法效率最高。則$[1,m]$列舉$n$,$[1,m]$列舉$i$。

以上說的情況是$a$與$p$互質的情況。那麼不互質該怎麼做呢?

我們變換一下形式:$a*a^{x-1}\equiv b\ (mod\ p)$。

移項,得$a*a^{x-1}+y*p=b$。

設$g=gcd(a,p)$,由裴蜀定理得,如果$b\ mod\ p≠0$,那麼此同餘方程無解。

左右兩邊同除$g$,得到$\frac{a}{g}*a^{x-1}+y*\frac{p}{g}=\frac{b}{g}$,即$a^{x-1}\equiv \frac{b}{g}*(\frac{a}{g})^{-1}\ (mod\ \frac{p}{g})$

重複上述步驟,直到$gcd(a,p)=1$為止,然後就可以用普通的BSGS求啦。

注意要特判一下,如果$b=1$或$p=1$或者某一時刻$(\frac{a}{g})^x=b'$,那麼直接返回$0$即可。

注意各種情況下的模數!!!被這個坑了好久QAQ。

程式碼:

#include<bits/stdc++.h>
#define int long long
using namespace std;
map<int,int> vis;
int a,p,b;
inline int gcd(int a,int b)
{
    if(!b) return a;
    return gcd(b,a%b); 
}
inline void exgcd(int a,int b,int &x,int &y)
{
    if (!b) x=1,y=0;
    else{
        exgcd(b,a
%b,x,y); int t=x;x=y;y=t-a/b*y; } } inline int inv(int a,int b) { int x,y; exgcd(a,b,x,y); return (x%b+b)%b; } inline int qcal(int a,int b,int p) { int res=1; while(b) { if (b&1) res=res*a%p; a=a*a%p; b>>=1; } return res; } inline int bsgs(int a,int b,int p) { vis.clear(); b%=p; int m=ceil(sqrt(p)); for (int i=1;i<=m;i++) b=b*a%p,vis[b]=i; b=1;int tmp=qcal(a,m,p); for (int i=1;i<=m;i++) { b=b*tmp%p; if (vis[b]) return (i*m-vis[b]+p)%p; } return -1; } inline int exbsgs(int a,int b,int p) { if (b==1||p==1) return 0; int g=gcd(a,p),k=0,na=1; while(g>1) { if (b%g!=0) return -1; b/=g;p/=g;na=na*(a/g)%p; k++; if (na==b) return k; g=gcd(a,p); } int f=bsgs(a,b*inv(na,p)%p,p); if (f==-1) return -1; return f+k; } signed main() { cin>>a>>p>>b; while(a||p||b) { a%=p;b%=p; int t=exbsgs(a,b,p); if (t==-1) puts("No Solution"); else printf("%lld\n",t); cin>>a>>p>>b; } return 0; }