问题描述

离散对数问题是,给定 a,b,nZ+a,b,n\in \Z^+,且 ana\perp n. 我们希望求解最小的 x[0,n)x\in [0,n) 满足

axb(modn)a^x\equiv b\pmod n

Baby-Step Giant-Step 算法

S=nS=\sqrt{n},那么 xx 可以表示为 x=AnBx=A\sqrt{n}-B 其中 A,BnA,B\le \sqrt{n}. 所以原式可以写作

ax=b(modn)aAnB=b(modn)aAn=baB(modn) \begin{aligned} a^x &= b \pmod{n}\\ a^{A\sqrt{n}-B}&=b\pmod{n}\\ a^{A\sqrt{n}} &= ba^{B} \pmod{n} \end{aligned}

其中 a,b,na,b,n 都是已知量,所以我们可以先 O(n)O(\sqrt{n}) 枚举 BB 计算 baBba^{B} 存到哈希表里,再用 O(n)O(\sqrt{n}) 枚举 AA 计算 aAna^{A\sqrt{n}} 并在哈希表里查.

为什么需要 ana\perp n

这是因为我们需要从第三行的式子倒推回第二行式子,也就是说需要 aBna^{B}\perp n,即 ana\perp n

Template Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int solve(int a, int b, int n) {
std::unordered_map<int, int> umap;
int S = std::ceil(std::sqrt(n * 1.0)); // 根号 n 上取整

int t = 1;
for (int B = 0; B <= S; B++) {
umap[1ll * b * t % p] = B;
// 对于 -B 部分,如果其余数相同,我们当然保留更大的 B.
t = 1ll * t * a % p;
} // 枚举 ba^B 部分

int fac = fpow(a, S, p); // 计算 a^S
t = 1;
int ans = (1ll << 31) - 1;
for (int A = 0; A <= S; A++) {
auto it = umap.find(t);
if (it != umap.end()) {
int val = norm(1ll * A * S - it->second, n - 1);
ans = std::min(ans, val); // 取最小值.
}
t = 1ll * t * fac % p;
} // 枚举 a^{AS} 部分

return ans >= n ? -1 : ans;
// 如果 ans >= n 说明循环内部没有产生更新,即没有找到解.
}

Extended Baby-Step Giant-Step 算法

ExBSGS 算法处理的问题也是类似,只不过不要求 a,na,n 互质了.

问题描述

给定 a,b,nZ+a,b,n\in\Z^+,求解

ax=b(modn)a^x=b\pmod{n}

其中 a,na,n 不一定互质.

大体思路是,通过 transform a,na,n 使得 a,na',n' 互质,把问题转化为 (a)x=b(modn)(a')^x=b'\pmod{n'} 然后用 BSGS 算法求解.

假设 d1=(a,n)d_1=(a,n),若 d1bd_1 \nmid b 则方程无解,否则我们可以变形为

ad1ax1=bd1(modnd1) \frac{a}{d_1}\cdot a^{x-1}=\frac{b}{d_1}\pmod{\frac{n}{d_1}}

我们的目标是让 ad1nd1\frac{a}{d_1}\perp \frac{n}{d_1},这样我们就可以把 ad1\frac{a}{d_1} 通过逆元放到等式右侧. 如果 aand1\frac{n}{d_1} 还是不互质,那么就再除,设 d2=(a,nd1)d_2=(a,\frac{n}{d_1}),则

a2d1d2ax2=bd1d2(modnd1d2) \frac{a^2}{d_1d_2}\cdot a^{x-2}=\frac{b}{d_1d_2}\pmod{\frac{n}{d_1d_2}}

我们不断的判断下去,直到 anDa\perp \frac{n}{D},令 D=i=1kdiD=\prod_{i=1}^k d_i. 于是方程变成了

akDaxk=bD(modnD) \frac{a^k}{D}\cdot a^{x-k}=\frac{b}{D}\pmod{\frac{n}{D}}

于是 akD\frac{a^k}{D} 在模 nD\frac{n}{D} 意义下有逆元,可以搬到等式的右侧. 然后使用普通 BSGS 算法求解出 xkx-k 后再把 kk 加回去即可.

Template Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
int solve(int a, int b, int n) {
a %= n;
b %= n;
// 特判:若 b==1 或 n==1 直接让 x=0 即可
if (b == 1 || n == 1) return 0;

// 快速幂函数
auto fpow = [](int b, int p, int m) {
int res = 1;
for (; p; p >>= 1, b = 1ll * b * b % m)
if (p & 1) res = 1ll * res * b % m;
return res;
};
// exgcd 求逆元,因为模数 n 可能是偶数
auto exgcd = [&](auto F, int a, int b, int &x, int &y) -> int {
if (!b) return x = 1, y = 0, a;
else {
int g = F(F, b, a % b, y, x);
y -= a / b * x;
return g;
}
};
// 取模,映射到 [0, n-1]
auto norm = [](int val, int n) { return val %= n, val += (val < 0 ? n : 0); };
// 用 exgcd 求解逆元
auto inv = [norm, exgcd](int val, int m) {
int x, y;
int g = exgcd(exgcd, val, m, x, y);
return g == 1 ? norm(x, m) : -1;
};
// 普通 Baby-Step Giant-Step 算法,处理的情况是 (a,n)=1
// 普通 BSGS 算法流程可以参考上文
auto bsgs = [fpow](int a, int b, int n) {
int m = 1 + std::sqrt(n * 1.0);
std::unordered_map<int, int> u;

for (int i = 0, t = b % n; i < m; i++) {
u[t] = i;
t = 1ll * t * a % n;
}
int factor = fpow(a, m, n);
int res = (1ll << 31) - 1;
for (int i = 0, t = 1 % n; i <= m; i++) {
auto it = u.find(t);
if (it != u.end()) {
int x = 1ll * i * m - it->second;
if (x >= 0) return x;
}
t = 1ll * t * factor % n;
}
return -1;
};

// 计算 D 值
// 这里其实一边计算 D 和 gcd,一边在 b,n,a 里面同时除掉 gcd.
// 同时,这里 if(ak == b) 也同时测试了前几个 k 值
int k = 0, ak = 1 % n;
while (true) {
int g = std::gcd(a, n);
if (g == 1) break;
if (b % g != 0) return -1;
n /= g, b /= g;
ak = 1ll * ak * (a / g) % n;
k++;
if (ak == b) return k;
a %= n;
}

// 这里把 a^k/D 移项到等式右侧
auto iv = inv(ak, n);
if (iv == -1) return -1;
b = 1ll * b * iv % n;

// 调用 BSGS 算法计算解
auto res = bsgs(a % n, b, n);
return res == -1 ? -1 : res + k; // 记得把 k 加回去
}

例题

【模板】扩展 BSGS/exBSGS

其实上文该说的已经说完了,注意关流同步.

2022 ICPC Asia-Manila Regional Contest L. LCG Manipulation