基础篇
BSGS(baby-step giant-step),即大步小步算法。常用于求解离散对数问题。形式化地说,该算法可以在 O ( p ) O(\sqrt{p}) O(p) 的时间内求解
a x ≡ b ( m o d p ) a^x \equiv b \pmod p ax≡b(modp)
其中 a ⊥ p a\perp p a⊥p。方程的解 x x x 满足 0 ≤ x < p 0 \le x < p 0≤x<p。(在这里需要注意,只要 a ⊥ p a\perp p a⊥p 就行了,不要求 p p p 是素数)
例题:3846 [TJOI2007] 可爱的质数/【模板】BSGS
求解过程
要求
a
x
≡
b
(
m
o
d
p
)
a^x \equiv b \pmod p
ax≡b(modp)
首先令
x
=
i
m
−
j
x=im-j
x=im−j
化为
(
a
m
)
i
≡
b
∗
a
j
(
m
o
d
p
)
(a^m)^i \equiv b*a^j\pmod p
(am)i≡b∗aj(modp)
j的取值范围是
[
0
,
m
)
[0,m)
[0,m),先枚举等号右边,并存入hash表中
i的取值范围是
[
1
,
m
]
[1,m]
[1,m],枚举等号左边,同时在hash表中查找是否有对应的值,找到的第一个为最小
#include<bits/stdc++.h>
#include <unordered_map>
using namespace std;
template<class...Args>
void debug(Args... args) {//Parameter pack
auto tmp = { (cout << args << ' ', 0)... };
cout << "\n";
}
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>pll;
typedef pair<int, int>pii;
const ll N = 1e5 + 5;
const ll INF = 0x7fffffff;
const ll MOD = 1e9 + 7;
ll qpow(ll x, ll n, ll mod) {
ll ans = 1;
while (n) {
if (n & 1)ans = (ans * x) % mod;
x = (x * x) % mod;
n >>= 1;
}
return ans % mod;
}
ll bsgs(ll a, ll b, ll p) {
if (b == 1) return 0;
ll m = ceil(sqrt(p));//时间复杂度为O(sqrt(n))
ll t = b;
unordered_map<ll, ll> dict;
dict[b] = 0;
for (int j = 1; j < m; ++j) {//计算b*aj对应的值将对应的j值存入hash表中
t = t * a % p;
dict[t] = j;
}
ll mi = qpow(a, m, p);//计算a^m
t = 1;
for (int i = 1; i <= m; ++i) {//枚举i
t = t * mi % p;
if (dict.count(t) != 0) return i * m - dict[t];
}
return -1;
}
int main() {
ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
ll b, n, p;
cin >> p >> b >> n;
ll ans = bsgs(b, n, p);
if (ans == -1)cout << "no solution\n";
else cout << ans << "\n";
return 0;
}
进阶篇
求解
x a ≡ b ( m o d p ) x^a \equiv b \pmod p xa≡b(modp)
其中 p p p 是个质数。
该模型可以通过一系列的转化为成 基础篇 中的模型,你可能需要了解关于 阶与原根 的知识。
由于式子中的模数 p p p 是一个质数,那么 p p p 一定存在一个原根 g g g。因此对于模 p p p 意义下的任意的数 x ( 0 ≤ x < p ) x\ (0\le x<p) x (0≤x<p) 有且仅有一个数 i ( 0 ≤ i < p − 1 ) i\ (0\le i<p-1) i (0≤i<p−1) 满足 x = g i x = g^i x=gi。
方法一
我们令 x = g c x=g^c x=gc, g g g 是 p p p 的原根(我们一定可以找到这个 g g g 和 c c c),问题转化为求解 ( g c ) a ≡ b ( m o d p ) (g^c)^a \equiv b \pmod p (gc)a≡b(modp)。稍加变换,得到
( g a ) c ≡ b ( m o d p ) (g^a)^c \equiv b \pmod p (ga)c≡b(modp)
于是就转换成了我们熟知的 BSGS 的基本模型了,可以在 O ( p ) O(\sqrt p) O(p) 解出 c c c,这样可以得到原方程的一个特解 x 0 ≡ g c ( m o d p ) x_0\equiv g^c\pmod p x0≡gc(modp)。
方法二
我们仍令 x = g c x=g^c x=gc,并且设 b = g t b=g^t b=gt,于是我们得到
g a c ≡ g t ( m o d p ) g^{ac}\equiv g^t\pmod p gac≡gt(modp)
方程两边同时取离散对数得到
a c ≡ t ( m o d φ ( p ) ) ac\equiv t\pmod{\varphi(p)} ac≡t(modφ(p))
我们可以通过 BSGS 求解 g t ≡ b ( m o d p ) g^t\equiv b\pmod p gt≡b(modp) 得到 t t t,于是这就转化成了一个线性同余方程的问题。这样也可以解出 c c c,求出 x x x 的一个特解 x 0 ≡ g c ( m o d p ) x_0\equiv g^c\pmod p x0≡gc(modp)。
找到所有解
在知道 x 0 ≡ g c ( m o d p ) x_0\equiv g^{c}\pmod p x0≡gc(modp) 的情况下,我们想得到原问题的所有解。首先我们知道 g φ ( p ) ≡ 1 ( m o d p ) g^{\varphi(p)}\equiv 1\pmod p gφ(p)≡1(modp),于是可以得到
∀ t ∈ Z , x a ≡ g c ⋅ a + t ⋅ φ ( p ) ≡ b ( m o d p ) \forall\ t \in \mathbb{Z},\ x^a \equiv g^{ c \cdot a + t\cdot\varphi(p)}\equiv b \pmod p ∀ t∈Z, xa≡gc⋅a+t⋅φ(p)≡b(modp)
于是得到所有解为
∀ t ∈ Z , a ∣ t ⋅ φ ( p ) , x ≡ g c + t ⋅ φ ( p ) a ( m o d p ) \forall\ t\in \mathbb{Z},a\mid t\cdot\varphi(p),\ x\equiv g^{c+\frac{t\cdot\varphi(p)}{a}}\pmod p ∀ t∈Z,a∣t⋅φ(p), x≡gc+at⋅φ(p)(modp)
对于上面这个式子,显然有 a gcd ( a , φ ( p ) ) ∣ t \frac{a}{\gcd(a,\varphi(p))} \mid t gcd(a,φ(p))a∣t。因此我们设 t = a gcd ( a , φ ( p ) ) ⋅ i t=\frac{a}{\gcd(a,\varphi(p))}\cdot i t=gcd(a,φ(p))a⋅i,得到
∀ i ∈ Z , x ≡ g c + φ ( p ) gcd ( a , φ ( p ) ) ⋅ i ( m o d p ) \forall \ i\in \mathbb{Z},x\equiv g^{c+\frac{\varphi(p)}{\gcd(a,\varphi(p))}\cdot i}\pmod p ∀ i∈Z,x≡gc+gcd(a,φ(p))φ(p)⋅i(modp)
这就是原问题的所有解。