大步小步算法
考虑这样一个问题:
题目 给定正整数 \(a,b,p\) 求满足 \(a^x\equiv b\pmod p\) 的所有正整数解 \(x\),保证 \(p\) 是素数。
分析 首先由费马小定理,\(a^{p-1}\equiv 1\pmod p\),也就是说暴力算法只需要枚举到 \(p-1\) 即可,我们考虑优化。
我们考虑只暴力求解 \(a^x\bmod p\) 的前 \(m\) 个值,也即 \(a^1\bmod p,a^2\bmod p,\cdots,a^m\bmod p\)。那么现在来考虑后面的数,将其 \(m\) 个一组分组(就是 \(m+1,m+2,\cdots,2m\) 一组,\(2m+1,2m+2,\cdots,3m\) 一组)。对于每一组这样考虑:如果
\[a^{km+t}\equiv b\pmod p
\]
这等价于
\[a^t\equiv b\cdot (a^{km})^{-1}\equiv b\cdot (a^{-m})^k\pmod p
\]
也就是说,我们在枚举每一个 \(k\) 的时候可以直接将 \(b\cdot (a^{-m})^k \bmod p\) 与每一个 \(a^i\) 对比,看是否有相同的,这可以用 \(map\) 实现。
这个算法被称为大步小步(Baby Step Giant Step, BSGS, 拔山盖世)算法。它的复杂度为 \(O((m+\frac n m)\log m)\),当 \(m=\sqrt{n}\) 时最优,为 \(O(\sqrt{n}\log n)\)
ll BSGS(ll a, ll b, ll mod)
{
map<ll, ll> Map;
ll m = sqrt(mod + 0.5), tmp = 1;
for(int i = 0; i < m; ++i) {
if(!Map.count(tmp)) Map[tmp] = i;
tmp = tmp * a % mod;
}
ll inv = qPow(tmp, mod - 2, mod);
for(int i = 0; i < m; ++i) {
if(Map.count(b)) return i * m + Map[b];
b = b * inv % mod;
}
return -1;
}