离散对数
定义
前置知识:阶与原根。
离散对数的定义方式和对数类似。取有原根的正整数模数
我们称这个
显然
性质
离散对数的性质也和对数有诸多类似之处。
性质
设
-
进而
-
若
也是模 的原根,则
证明
-
令
,则 . 又令 ,则 . 故
,即 -
注意到
大步小步算法
目前离散对数问题仍不存在多项式时间经典算法(离散对数问题的输入规模是输入数据的位数)。在密码学中,基于这一点人们设计了许多非对称加密算法,如 Ed25519。
在算法竞赛中,BSGS(baby-step giant-step,大步小步算法)常用于求解离散对数问题。形式化地说,对
其中
算法描述
令
我们已知的是 hash
/map
存下来,然后逐一计算
注意到 map
则多一个
为什么要求 与 互质
注意到我们求出的是
进阶篇
对
该问题可以转化为 BSGS 求解的问题。
由于式子中的模数
方法一
我们令
于是就转换成了 BSGS 的基本模型了,可以在
方法二
我们仍令
方程两边同时取离散对数得到
我们可以通过 BSGS 求解
找到所有解
在知道
于是得到所有解为
对于上面这个式子,显然有
这就是原问题的所有解。
实现
下面的代码实现的找原根、离散对数解和原问题所有解的过程。
参考代码
int gcd(int a, int b) { return a ? gcd(b % a, a) : b; }
int powmod(int a, int b, int p) {
int res = 1;
while (b > 0) {
if (b & 1) res = res * a % p;
a = a * a % p, b >>= 1;
}
return res;
}
// Finds the primitive root modulo p
int generator(int p) {
vector<int> fact;
int phi = p - 1, n = phi;
for (int i = 2; i * i <= n; ++i) {
if (n % i == 0) {
fact.push_back(i);
while (n % i == 0) n /= i;
}
}
if (n > 1) fact.push_back(n);
for (int res = 2; res <= p; ++res) {
bool ok = true;
for (int factor : fact) {
if (powmod(res, phi / factor, p) == 1) {
ok = false;
break;
}
}
if (ok) return res;
}
return -1;
}
// This program finds all numbers x such that x^k=a (mod n)
int main() {
int n, k, a;
scanf("%d %d %d", &n, &k, &a);
if (a == 0) return puts("1\n0"), 0;
int g = generator(n);
// Baby-step giant-step discrete logarithm algorithm
int sq = (int)sqrt(n + .0) + 1;
vector<pair<int, int>> dec(sq);
for (int i = 1; i <= sq; ++i)
dec[i - 1] = {powmod(g, i * sq * k % (n - 1), n), i};
sort(dec.begin(), dec.end());
int any_ans = -1;
for (int i = 0; i < sq; ++i) {
int my = powmod(g, i * k % (n - 1), n) * a % n;
auto it = lower_bound(dec.begin(), dec.end(), make_pair(my, 0));
if (it != dec.end() && it->first == my) {
any_ans = it->second * sq - i;
break;
}
}
if (any_ans == -1) return puts("0"), 0;
// Print all possible answers
int delta = (n - 1) / gcd(k, n - 1);
vector<int> ans;
for (int cur = any_ans % delta; cur < n - 1; cur += delta)
ans.push_back(powmod(g, cur, n));
sort(ans.begin(), ans.end());
printf("%zu\n", ans.size());
for (int answer : ans) printf("%d ", answer);
}
扩展篇(扩展 BSGS)
对
其中
当
具体地,设
如果
同理,这样不停的判断下去,直到
记
由于
注意,不排除解小于等于
习题
- SPOJ MOD 模板
- SDOI2013 随机数生成器
- SGU261 Discrete Roots 模板
- SDOI2011 计算器 模板
- Luogu4195【模板】exBSGS/Spoj3105 Mod 模板
- Codeforces - Lunar New Year and a Recursive Sequence
- LOJ6542 离散对数 index calculus 方法,非模板
本页面部分内容以及代码译自博文 Дискретное извлечение корня 与其英文翻译版 Discrete Root。其中俄文版版权协议为 Public Domain + Leave a Link;英文版版权协议为 CC-BY-SA 4.0。
参考资料
- Discrete logarithm - Wikipedia
- 潘承洞,潘承彪。初等数论。
- 冯克勤。初等数论及其应用。
创建日期: 2018年7月11日