BSGS
基础篇¶
BSGS(baby-step giant-step),即大步小步算法。常用于求解离散对数问题。形式化地说,该算法可以在
其中
算法描述¶
令
我们已知的是 hash
/map
存下来,然后逐一计算
注意到 map
则多一个
进阶篇¶
求解
其中
该模型可以通过一系列的转化为成 基础篇 中的模型,你可能需要了解关于 阶与原根 的知识。
由于式子中的模数
方法一¶
我们令
于是就转换成了我们熟知的 BSGS 的基本模型了,可以在
方法二¶
我们仍令
方程两边同时取离散对数得到
我们可以通过 BSGS 求解
找到所有解¶
在知道
于是得到所有解为
对于上面这个式子,显然有
这就是原问题的所有解。
实现¶
下面的代码实现的找原根、离散对数解和原问题所有解的过程。
参考代码
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 | 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("%d\n", ans.size());
for (int answer : ans) printf("%d ", answer);
}
|
扩展篇¶
接下来我们求解
其中
当
具体地,设
如果
同理,这样不停的判断下去。直到
记
由于
注意,不排除解小于等于
习题¶
- 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。
build本页面最近更新:,更新历史
edit发现错误?想一起完善? 在 GitHub 上编辑此页!
people本页面贡献者:OI-wiki
copyright本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用