Definition
Prerequisite knowledge: order and primitive roots.
Discrete logarithms are defined in a similar way to logarithms. Take the positive integer modulus with primitive roots, and let one of its primitive roots be . For the integer that satisfies , we know that there must be a unique integer such that
We call this the discrete logarithm of modulo , denoted as , or without causing confusion.
Apparently , .
Properties
The properties of discrete logarithms also have many similarities with logarithms.
Properties
Suppose is the primitive root modulo , and , then:
-
then
-
If is also a primitive root modulo , then
-
Proof
-
-
Let , then . Let , then .
, so , that is,
-
noted
Baby-Step Giant-Step Algorithm
At present, there is still no polynomial time classical algorithm for the discrete logarithm problem (the input size of the discrete logarithm problem is the number of digits of the input data). In cryptography, based on this, people have designed many asymmetric encryption algorithms, such as Ed25519.
In algorithm competitions, BSGS (baby-step giant-step algorithm) is often used to solve discrete logarithm problems. Formally speaking, for , the algorithm can be solved in time
Here . The solution satisfies . (Note that is not necessarily a prime number)
Algorithm Description
Let , among them , then we have , and with a slight transformation, we have .
What we know is , so we can first calculate all the values of on the right side of the equation, enumerate , save it with hash/map, then calculate one by one, enumerate , and find whether there is an equal value , so we can get all , .
Note that is smaller than , so the time complexity is , and using map will result in one more .
Why does > [!note]- require that and are relatively prime?
Note that what we find is . We need to ensure that can be pushed back to . The latter formula is obtained by dividing the left and right sides of the front formula by , so there must be , that is, .
Extended BSGS Algorithm
For , solve
Here are not necessarily coprime.
When , has an inverse element modulo , so it can be solved using the BSGS algorithm. So we tried to find ways to make them mutually prime.
Specifically, let . If , then the original equation has no solution. Otherwise we divide the equation by at the same time and get
If and are still not mutually prime, divide again, let . If , the equation has no solution; otherwise divide by at the same time to get
In the same way, continue to judge in this way until .
Remember , so the equation becomes like this:
Since , follows. In this way, has an inverse element, so move it to the right-hand side. This is an ordinary BSGS problem. So after solving and adding , it is the solution of the original equation.
Note that it is not excluded that the solution is less than or equal to , so before eliminating factors, do the enumeration and directly verify , so that this situation can be avoided.
Fast Discrete Logarithm with Range Preprocessing
The time complexity of the previous BSGS algorithm is a single , and the efficiency is low when the query magnitude is large. If the modulus solved each time is a fixed prime number , we have a fast algorithm based on range preprocessing.
We already know , so we can only calculate the discrete logarithm of all prime numbers through the BSGS algorithm. The discrete logarithm of the composite number can be converted into the sum of the discrete logarithms of several known prime numbers through this formula. At this time, the complexity is still not optimal. We consider preprocessing only a part of the discrete logarithms. Specifically, we preprocess the discrete logarithms from to . Note that the BSGS block length at this time cannot be taken , because the complexity of the BSGS preprocessing (insertion into the hash table) part is , and the query requires a total of times, so the total time complexity is , and is optimal at this time. Using the prime number theorem to , the total preprocessing time complexity can be balanced to .
Next is how to find the answer. Assume that the current requirement is , if , return directly, otherwise set , then , , , thus
Note that , so we only need to recursively calculate the discrete logarithm of .
We can also consider another expression of , noting , then , thus
We have , so we only need to recursively calculate the discrete logarithm of .
Combining these two calculation methods, we have , so the smaller recursive calculation can achieve the query complexity of .
So far we have obtained an algorithm with a time complexity of .
Luogu11175 [Template] Fast discrete logarithm based on range preprocessing
#include <cmath>
#include <iostream>
#include <unordered_map>
using namespace std;
const int N = 1'000'000; // sqrt(P * sqrt(P) / ln(P))
unordered_map<int, int> M;
int Lg[N], p[N], t;
bool vis[N];
int P, g, B, sq, LP_1, inv;
int fadd(int x, int y, int P) {
x += y;
if (x >= P) x -= P;
return x;
}
int fsub(int x, int y, int P) {
x -= y;
if (x < 0) x += P;
return x;
}
int fmul(int x, int y, int P) { return 1ll * x * y % P; }
int qpow(int x, int y) {
int ans = 1;
while (y) {
if (y & 1) ans = fmul(ans, x, P);
x = fmul(x, x, P);
y >>= 1;
}
return ans;
}
int calc(int x) {
int s = x;
for (int i = 0; i <= P / B; ++i) {
if (M.find(s) != M.end()) return i * B + M[s];
s = fmul(s, inv, P);
}
return -1;
}
void init() {
int s = 1;
for (int i = 0; i < B; ++i) {
if (M.find(s) != M.end()) break;
M[s] = i, s = fmul(s, g, P);
}
inv = qpow(qpow(g, B), P - 2);
for (int i = 2; i <= sq; ++i) {
if (!vis[i]) {
p[++t] = i;
Lg[i] = calc(i);
}
for (int j = 1; j <= t && p[j] * i <= sq; ++j) {
vis[p[j] * i] = true;
Lg[p[j] * i] = fadd(Lg[p[j]], Lg[i], P - 1);
if (i % p[j] == 0) break;
}
}
}
int solve(int y) {
if (y <= sq) return Lg[y];
int v = P / y, r = P % y;
if (r < y - r) return fadd(fsub(solve(r), Lg[v], P - 1), LP_1, P - 1);
return fsub(solve(y - r), Lg[v + 1], P - 1);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> P >> g;
sq = sqrt(P) + 1;
B = sqrt(1ll * P * sqrt(P) / log(P));
init();
LP_1 = (P - 1) / 2; // g ^ LP_1 = P - 1 (mod P)
int T;
cin >> T;
while (T--) {
int x;
cin >> x;
cout << solve(x) << '\n';
}
return 0;
}
Exercises
- SPOJ MOD template
- SDOI2013 random number generator
- SGU261 Discrete Roots template
- SDOI2011 calculator template
- Luogu4195【template】exBSGS/Spoj3105 Mod template
- Codeforces - Lunar New Year and a Recursive Sequence
- LOJ6542 discrete logarithm index calculus method, non-template
**Part of the content and code of this page are adapted from the blog post Дискретное извлечение корня and its English-language edition Discrete Root. The copyright agreement for the Russian version is Public Domain + Leave a Link; the copyright agreement for the English version is CC-BY-SA 4.0. **
References
- Discrete logarithm - Wikipedia
- Pan Chengdong, Pan Chengbiao. Elementary number theory.
- Feng Keqin. Elementary number theory and its applications.