大步小步算法

考虑这样一个问题:

题目 给定正整数 \(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;
}
posted @ 2020-03-30 23:19  whx1003  阅读(704)  评论(0编辑  收藏  举报