-
Notifications
You must be signed in to change notification settings - Fork 62
/
Copy pathdiscrete_logarithm.cpp
76 lines (59 loc) · 1.83 KB
/
discrete_logarithm.cpp
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
// {{{ data_structures/splitmix64_hash_map.cpp }}}
#include <cstdint>
#include <numeric>
int product(int x, int y, int MOD) {
return int(int64_t(x) * y % MOD);
}
int mod_pow(int x, int k, int MOD) {
if (!k) return 1;
if (k&1) return product(x, mod_pow(x, k - 1, MOD), MOD);
return mod_pow(product(x, x, MOD), k / 2, MOD);
}
int totient(int v) {
int tot = v;
for (int p = 2; p * p <= v; p++) if (v % p == 0) {
tot = tot / p * (p - 1);
while (v % p == 0) v /= p;
}
if (v > 1) tot = tot / v * (v - 1);
return tot;
}
/* Returns the smallest K >= 0 such that init * pow(x, K) === y modulo MOD.
* Returns -1 if no such K exists. Runs in O(sqrt(MOD)).
*/
int discrete_log(int x, int y, int MOD, int init = 1) {
if (x == 0)
return y == 1 ? 0 : y == 0 ? MOD > 1 : -1;
int prefix = 0;
while (init != y && std::gcd(init, MOD) != std::gcd(product(init, x, MOD), MOD)) {
init = product(init, x, MOD);
prefix++;
}
if (init == y)
return prefix;
if (std::gcd(init, MOD) != std::gcd(y, MOD))
return -1;
MOD = MOD / std::gcd(init, MOD);
x %= MOD;
y %= MOD;
init %= MOD;
int subgroup_order = totient(MOD);
y = product(y, mod_pow(init, subgroup_order - 1, MOD), MOD);
int step_size = 0;
while (step_size * step_size < subgroup_order)
step_size++;
umap<int, int> table;
int baby_step = 1;
for (int i = 0; i < step_size; i++) {
table[baby_step] = i;
baby_step = product(baby_step, x, MOD);
}
int giant_step = mod_pow(x, subgroup_order - step_size, MOD);
for (int i = 0; i < step_size; i++) {
auto it = table.find(y);
if (it != table.end())
return prefix + i * step_size + it->second;
y = product(y, giant_step, MOD);
}
return -1;
}