bsgs
bsgs,北上广深,拔山盖世,蓝超巨星(blue super giant star)。大概是\(O(\sqrt n)\)求解模意义下离散对数的一个算法。经典的平衡复杂度思想。
本质是根号分治。
离散对数,也就是长这样的一个东西:
\[a^x\equiv b\pmod p
\]
其中\(p\)是个质数,给你\(a,b,p,\)求最小的正整数\(x\)。
首先我们根据费马小定理知道\(a^x\)是个循环的,而且一个循环节是\(p-1\)。当然不一定是最小循环节。当然,我们不能暴力搞。这时我们就用到了一个重要思想:平衡复杂度的思想。
具体的,我们随便设个\(t\)(先在这挂着,取值一会再说),然后开始导柿子:
\[a^x\equiv b \pmod p
\]
设\(x=i\cdot t-j\)。
\[a^{i\cdot t-j}\equiv b \pmod p
\]
\[a^{i\cdot t}\equiv b\cdot a^j \pmod p
\]
对这个柿子,我们可以预处理每个\(a^j\),然后枚举\(i\)来找。显然\(j<t\)。当\(t=\sqrt n\)时,复杂度最低,为\(O(\sqrt n)\)。
知道原理之后代码其实很好写。
unordered_map<int,int>mp;
int bsgs(int a,int b){
mp.clear();
int sq=sqrt(mod)+1;
int tmp=b;
for(int i=0;i<sq;i++){
mp[tmp]=i;//先预处理所有的j放到哈希表里
tmp=1ll*tmp*a%mod;
}
a=qpow(a,sq);
if(!a)return b==0?1:-1;//特判一下有无解
for(int i=1,val=1;i<=sq;i++){//枚举所有i
val=1ll*val*a%mod;
if(mp.find(val)!=mp.end())return i*sq-mp[val];//查找能不能找到对应的j
}
return -1;
}
然后是 exbsgs,用于 \(a,p\) 不互质的情况。设 \(d=\gcd(a,p)\),则
\[a^x\equiv b\pmod p
\]
可以变成
\[\frac{a^x}d\equiv \frac bd\pmod {\frac pd}
\]
换个写法:
\[\frac ada^{x-1}\equiv \frac bd\pmod {\frac pd}
\]
如果 \(d\not|\ b\) 则无解。然后一直消掉 \(d\),直到 \(a,p\) 互质,跑遍 bsgs 再加上消的次数即可。
int exbsgs(int a,int b,int mod){
if(b==1||mod==1)return 0;
int d=gcd(a,mod),k=0,mul=1;
while(d!=1){
if(b%d!=0)return -1;
k++;b/=d;mod/=d;mul=1ll*mul*(a/d)%mod;
if(mul==b)return k;
d=gcd(a,mod);
}
int f=bsgs(a,1ll*b*inv(mul,mod)%mod,mod);
if(f==-1)return -1;
return f+k;
}
快踩