1 条题解
-
0
好的,我们先明确题意与已知条件。
题意回顾
- 已知固定的质数 (P) 和原根 (g)。
- Alice 随机生成 (a),发送 (A = g^a \bmod P)。
- Bob 随机生成 (b),发送 (B = g^b \bmod P)。
- 密钥 (K = g^{ab} \bmod P),Alice 算 (K = B^a \bmod P),Bob 算 (K = A^b \bmod P)。
现在攻击者窃听到 (A) 和 (B),知道 (g) 和 (P),要算出 (K)。
问题本质
已知 (A \equiv g^a \pmod{P}),(B \equiv g^b \pmod{P}),
我们不知道 (a, b),但要算出 (K \equiv g^{ab} \pmod{P})。由于 (K \equiv A^b \equiv B^a \pmod{P}),
如果我们能求出 (a) 或 (b),就能直接计算 (K)。
解法思路
从 (A = g^a \bmod P) 求 (a),这是离散对数问题(DLP)。
给定 (P) 是质数,且 (g) 是原根,(a) 的范围是 (1) 到 (P-1)。虽然 (P < 2^{31}),暴力枚举 (a) 最大需要 (O(P)),不行((P) 最多约 (21) 亿)。
我们需要更快的算法。
Baby-step giant-step(BSGS)算法
对 (A \equiv g^a \pmod{P}) 求 (a),用 BSGS 可以在 (O(\sqrt{P} \log P)) 时间求解。
由于 (P) 最大约 (2^{31}),(\sqrt{P} \approx 46340),可行。步骤:
- 设 (m = \lceil \sqrt{P-1} \rceil)。
- 预计算 (g^j \bmod P) 对 (j = 0,1,\dots,m-1),存到哈希表(值 → 指数)。
- 计算 (g^{-m} \bmod P)。
- 令 (t = A),对 (i=0) 到 (m-1):
- 检查 (t) 是否在表中,如果在且 (j) 对应,则 (a = i m + j)。
- 更新 (t = t \cdot g^{-m} \bmod P)。
这样我们可以求出 (a) 或者 (b),然后 (K = B^a \bmod P)。
优化
注意到我们并不需要同时求 (a) 和 (b)。
如果我们求出 (a),那么 (K = B^a \bmod P);
如果求出 (b),那么 (K = A^b \bmod P)。实际上,由于 (P) 固定,(g) 固定,我们可以对 (n) 次查询中的每个 (A) 求一次 (a),然后算 (K)。
但是 BSGS 预计算只依赖 (g) 和 (P),所以我们可以只做一次预计算,然后对每个 (A) 查询 (a)。
算法步骤
- 读入 (g, P, n)。
- 用 BSGS 预计算 (g^j \bmod P) 表((j=0) 到 (m-1))。
- 对每个查询 ((A, B)):
- 用 BSGS 从 (A) 求出 (a)。
- 计算 (K = B^a \bmod P)。
- 输出 (K)。
时间复杂度
- 预计算 (O(\sqrt{P})) 时间和空间。
- 每个查询 (O(\sqrt{P} \log P))(实际上用哈希表是 (O(\sqrt{P})))。
- (n \le 20),完全可行。
样例验证
样例:
3 31 3 27 16 21 3 9 26-
对 (A=27),求 (a):
(3^a \equiv 27 \pmod{31})
已知 (3^3 = 27),所以 (a=3)。
(K = 16^3 \bmod 31 = 4096 \bmod 31 = 4)。 -
对 (A=21),求 (a):
(3^a \equiv 21 \pmod{31})
经计算 (3^{13} \bmod 31 = 21),所以 (a=13)。
(K = 3^{13} \bmod 31 = 21)。 -
对 (A=9),求 (a):
(3^a \equiv 9 \pmod{31})
(3^2 = 9),所以 (a=2)。
(K = 26^2 \bmod 31 = 676 \bmod 31 = 25)。
输出
4, 21, 25,与样例一致。
实现细节
- 用
unordered_map存储 (g^j \to j)。 - 用快速幂取模。
- 注意 (P) 在
int范围内,但乘法可能溢出,要用long long做中间运算。
代码框架(C++):
#include <iostream> #include <unordered_map> #include <cmath> using namespace std; using ll = long long; ll powmod(ll a, ll b, ll p) { ll res = 1; while (b) { if (b & 1) res = res * a % p; a = a * a % p; b >>= 1; } return res; } ll discrete_log(ll g, ll A, ll P) { // BSGS, 返回 a 满足 g^a = A mod P ll m = ceil(sqrt(P)); unordered_map<ll, ll> table; ll e = 1; for (ll j = 0; j < m; j++) { table[e] = j; e = e * g % P; } ll factor = powmod(g, P - 1 - m, P); // g^(-m) mod P e = A; for (ll i = 0; i < m; i++) { if (table.find(e) != table.end()) { return i * m + table[e]; } e = e * factor % P; } return -1; // 不应该发生,因为 g 是原根且 A 在 [1,P-1] } int main() { ll g, P, n; cin >> g >> P >> n; while (n--) { ll A, B; cin >> A >> B; ll a = discrete_log(g, A, P); ll K = powmod(B, a, P); cout << K << endl; } return 0; }
最终答案:
用 BSGS 求离散对数,然后快速幂算 (K),对每组 (A,B) 输出。
- 1
信息
- ID
- 6108
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者