BSGS 學習筆記
阿新 • • 發佈:2020-07-24
問題:求$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; }