from wikipedia
In group theory, a branch of mathematics, the baby-step giant-step is a meet-in-the-middle algorithm for computing the discrete logarithm or order of an element in a finite abelian group due to Daniel Shanks. The discrete log problem is of fundamental importance to the area of public key cryptography.
The baby-step giant-step algorithm can solve the problem of finding discrete logorithms in
O
(
n
)
O(\sqrt{n}\space)
O(n )time, significantly faster than the
O
(
n
)
O(n)
O(n) time complexity of the naive trial-and-error approach.
BSGS
The problem we are aiming to solve using BSGS can be properly framed as follow:
Given a cyclic group G \mathbb{G} G of order n \mathbb{n} n, a generator a a a of the group and a group element b b b, we are to find an integer x x x such that a x = b a^x=b ax=b.
In the form of modular arithmetic, this is essentially solving a x ≡ b ( m o d p ) a^x\equiv b\pmod{p} ax≡b(modp), where gcd ( a , p ) = 1 \gcd(a,p)=1 gcd(a,p)=1.
We let x = c ⌈ p ⌉ − d x = c\lceil \sqrt p \rceil - d x=c⌈p⌉−d ( 0 ≤ c , d ≤ ⌈ p ⌉ 0\le c,d \le \lceil \sqrt p\rceil 0≤c,d≤⌈p⌉).
Substitute back to the original equation, we have
a
c
⌈
p
⌉
−
d
≡
b
(
m
o
d
p
)
a
c
⌈
p
⌉
≡
b
a
d
(
m
o
d
p
)
\begin{aligned} a^{c\lceil \sqrt p\rceil -d} &\equiv b \pmod p\\ a^{c\lceil \sqrt p\rceil} &\equiv ba^d \pmod p \end{aligned}
ac⌈p⌉−dac⌈p⌉≡b(modp)≡bad(modp)
Next, we compute all possible
⌈
p
⌉
{\lceil \sqrt p\rceil}
⌈p⌉ values of
b
a
d
ba^d
bad and store them in a hash table. Then, we compute all possible
⌈
p
⌉
{\lceil \sqrt p\rceil}
⌈p⌉ values of
(
a
⌈
p
⌉
)
c
(a^{\lceil \sqrt p\rceil})^c
(a⌈p⌉)c and find if there is any
b
a
d
ba^d
bad with equal value in
O
(
1
)
O(1)
O(1) time, and corresponding value of
x
x
x can thus be found. The overall time complexity is therefore
O
(
p
)
O(\sqrt p)
O(p).
The idea of BSGS is rather simple, and it is based on a space-time trade-off.
Extension: Finding a discrete root
Next up, we want to find all x x x which satisfies the equation x a ≡ b ( m o d p ) x^a\equiv b\pmod p xa≡b(modp), where p p p is a prime number.
It would require a few steps and some preliminary knowledge about order and primitive root to convert the problem to the typical problem we solve with BSGS.
Suppose prime number p p p has a primitive root g g g, we can solve the equation ( g a ) c ≡ b ( m o d p ) (g^a)^c\equiv b\pmod p (ga)c≡b(modp) for x ≡ g c ( m o d p ) x\equiv g^c\pmod p x≡gc(modp) instead in O ( p ) O(\sqrt p) O(p) time using BSGS. We can always find such c c c since the cyclic group generated by g g g, { g , g 2 , ⋯ , g ϵ p ( g ) − 1 , 1 } \lbrace g,g^2,\cdots,g^{\epsilon_p(g)-1},1 \rbrace {g,g2,⋯,gϵp(g)−1,1} has an order of φ ( p ) = p − 1 \varphi(p)=p-1 φ(p)=p−1 and hence should cover every possible remainder of p p p, unless b = 0 b=0 b=0 which would have made the problem trivial to begin with. Now we have one solution determined which is x 0 = g c x_0=g^c x0=gc.
To obtain all the solutions, since we have g p − 1 ≡ 1 ( m o d p ) g^{p-1}\equiv1\pmod p gp−1≡1(modp) by Fermat’s Little Theorem, we have g c × ( g p − 1 ) n ≡ b ( m o d p ) g^c\times(g^{p-1})^n\equiv b\pmod p gc×(gp−1)n≡b(modp), and x c + n ( p − 1 ) a = b ( m o d p ) x^{c+\frac{n(p-1)}{a}}=b\pmod p xc+an(p−1)=b(modp), where a ∣ n ( p − 1 ) a|n(p-1) a∣n(p−1).
To find all such n n n, we factor out g c d ( a , p − 1 ) gcd(a,p-1) gcd(a,p−1),and since gcd ( a gcd ( a , p − 1 ) , p − 1 gcd ( a , p − 1 ) ) = 1 , \gcd(\frac{a}{\gcd(a,p-1)},\frac{p-1}{\gcd(a,p-1)})=1, gcd(gcd(a,p−1)a,gcd(a,p−1)p−1)=1, we are left with a gcd ( a , p − 1 ) ∣ n \frac{a}{\gcd(a,p-1)}|n gcd(a,p−1)a∣n. We can thus let n = m × a gcd ( a , p − 1 ) n=m\times\frac{a}{\gcd(a,p-1)} n=m×gcd(a,p−1)a, where m ∈ Z . m\in\mathbb{Z}. m∈Z. Hence, all solutions x x x to the equations are x = g c + m a gcd ( a , p − 1 ) x=g^{c+\frac{ma}{\gcd(a,p-1)}} x=gc+gcd(a,p−1)ma, where m ∈ Z . m\in\mathbb{Z}. m∈Z.
Extension: a more general case
Through a little tweak, we can use BSGS to solve a x ≡ b ( m o d p ) a^x\equiv b\pmod{p} ax≡b(modp) with no restriction on a a a and p p p.
Factor out
g
c
d
(
a
,
p
)
=
f
1
gcd(a,p)=f_1
gcd(a,p)=f1 from the origianl equation, we have
a
f
1
×
a
x
−
1
≡
b
(
m
o
d
b
f
1
)
\frac{a}{f_1}\times a^{x-1}\equiv b\pmod{\frac{b}{f1}}
f1a×ax−1≡b(modf1b).
We carry out this process for
k
k
k times, after which
gcd
(
a
,
p
∏
i
=
1
k
f
i
)
=
1.
\gcd(a,\frac{p}{\prod_{i=1}^k{f_i}})=1.
gcd(a,∏i=1kfip)=1.
Notice that this process will be carried out for at most
l
o
g
2
(
p
)
log_2(p)
log2(p) times.
Now we have a k ∏ i = 1 k f i × a x − k ≡ b ( m o d p ∏ i = 1 k f i ) \frac{a^k}{\prod_{i=1}^k{f_i}}\times a^{x-k}\equiv b\pmod{\frac{p}{\prod_{i=1}^k{f_i}}} ∏i=1kfiak×ax−k≡b(mod∏i=1kfip), and we can find the multiplicative inverse of a k ∏ i = 1 k f i \frac{a^k}{\prod_{i=1}^k{f_i}} ∏i=1kfiak modulo p ∏ i = 1 k f i \frac{p}{\prod_{i=1}^k{f_i}} ∏i=1kfip using the extended euclidean algorithm since gcd ( a , p ∏ i = 1 k f i ) = 1. \gcd(a,\frac{p}{\prod_{i=1}^k{f_i}})=1. gcd(a,∏i=1kfip)=1.
The equation can then be reduced to the simple case
a
x
−
k
≡
b
×
(
a
k
∏
i
=
1
k
f
i
)
−
1
(
m
o
d
p
∏
i
=
1
k
f
i
)
a^{x-k}\equiv b\times(\frac{a^k}{\prod_{i=1}^k{f_i}})^{-1}\pmod{\frac{p}{\prod_{i=1}^k{f_i}}}
ax−k≡b×(∏i=1kfiak)−1(mod∏i=1kfip) which we can solve by BSGS. Notice that we might end up with a solution for
x
−
k
<
k
x-k<k
x−k<k, in this case we only need to add multiple copies of
ϵ
p
∏
i
=
1
k
f
i
(
a
)
\epsilon_{\frac{p}{\prod_{i=1}^k{f_i}}}(a)
ϵ∏i=1kfip(a) to the answer until it is no longer negative.
Implementation
Below is a short implementation of the extended BSGS algorithm in C++
int exgcd(int a, int b, int& x, int& y)//extended euclidean algorithm to find the multiplicative inverse
{
if (!b)
{
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int bsgs(int a, int b, int p)//the basic BSGS algorithm
{
if (1 % p == b % p) return 0;//special case
int k = sqrt(p) + 1;//meet in the middle approach
unordered_map<int, int> hash;//hash map
for (int i = 0, j = b % p; i < k; i ++ )
{
hash[j] = i;
j = (LL)j * a % p;
}
int ak = 1;
for (int i = 0; i < k; i ++ ) ak = (LL)ak * a % p;
for (int i = 1, j = ak; i <= k; i ++ )
{
if (hash.count(j)) return i * k - hash[j];
j = (LL)j * ak % p;
}
return -inf;//no answer
}
int exbsgs(int a, int b, int p)//extended BSGS
{
b = (b % p + p) % p;
if (1 % p == b % p) return 0;
int x, y;
int d = exgcd(a, p, x, y);
if (d > 1)
{
if (b % d) return -inf;
exgcd(a / d, p / d, x, y);
return exbsgs(a, (LL)b / d * x % (p / d), p / d) + 1;
}
return bsgs(a, b, p);
}