Baby-Step Giant-Step (BSGS)
The core idea is beautifully simple once you see it — it's essentially a meet-in-the-middle strategy applied to the discrete logarithm problem.
The Problem
You want to find x x x such that g x = h ( m o d p ) g^x = h \pmod{p} g x = h ( mod p ) . Naively, you'd try x = 0 , 1 , 2 , … , p − 1 x = 0, 1, 2, \dots, p-1 x = 0 , 1 , 2 , … , p − 1 which takes O ( p ) O(p) O ( p ) time where p p p is the group order.
The Key Insight
Write x = i ⋅ m + j x = i \cdot m + j x = i ⋅ m + j where m = ⌈ p ⌉ m = \lceil\sqrt{p}\rceil m = ⌈ p ⌉ and 0 ≤ i , j < m 0 \le i, j < m 0 ≤ i , j < m . Then:
g i m + j = h g^{im + j} = h g im + j = h
g j = h ⋅ ( g − m ) i g^j = h \cdot (g^{-m})^i g j = h ⋅ ( g − m ) i
You've split one search over p p p values into two searches over p \sqrt{p} p values each.
The Two Phases
Baby steps — Precompute and store g j g^j g j for j = 0 , 1 , … , m − 1 j = 0, 1, \dots, m-1 j = 0 , 1 , … , m − 1 in a hash table. These are small, incremental steps through the group.
Giant steps — Compute h ⋅ ( g − m ) i h \cdot (g^{-m})^i h ⋅ ( g − m ) i for i = 0 , 1 , 2 , … i = 0, 1, 2, \dots i = 0 , 1 , 2 , … and check if it's in your table. Each iteration jumps by m m m positions — hence "giant" steps.
When you find a collision, you've got g j = h ⋅ g − i m g^j = h \cdot g^{-im} g j = h ⋅ g − im , so h = g i m + j h = g^{im+j} h = g im + j and x = i m + j x = im + j x = im + j .
Walkthrough: 5 x ≡ 20 ( m o d 53 ) 5^x \equiv 20 \pmod{53} 5 x ≡ 20 ( mod 53 )
Here g = 5 g = 5 g = 5 , h = 20 h = 20 h = 20 , and the group order is p = 52 p = 52 p = 52 (since ( Z / 53 Z ) ∗ (\mathbb{Z}/53\mathbb{Z})^* ( Z /53 Z ) ∗ has order 53 − 1 = 52 53 - 1 = 52 53 − 1 = 52 ).
Setup. Compute m = ⌈ 52 ⌉ = 8 m = \lceil\sqrt{52}\rceil = 8 m = ⌈ 52 ⌉ = 8 .
Baby steps — build a table of g j m o d 53 g^j \bmod 53 g j mod 53 for j = 0 , … , 7 j = 0, \dots, 7 j = 0 , … , 7 :
j j j 5 j m o d 53 5^j \bmod 53 5 j mod 53 0 1 1 5 2 25 3 19 4 42 5 51 6 43 7 3
Store as a lookup: { 1 ↦ 0 , 5 ↦ 1 , 25 ↦ 2 , 19 ↦ 3 , 42 ↦ 4 , 51 ↦ 5 , 43 ↦ 6 , 3 ↦ 7 } \{1 \mapsto 0,\; 5 \mapsto 1,\; 25 \mapsto 2,\; 19 \mapsto 3,\; 42 \mapsto 4,\; 51 \mapsto 5,\; 43 \mapsto 6,\; 3 \mapsto 7\} { 1 ↦ 0 , 5 ↦ 1 , 25 ↦ 2 , 19 ↦ 3 , 42 ↦ 4 , 51 ↦ 5 , 43 ↦ 6 , 3 ↦ 7 } .
Giant steps — we need g − m = 5 − 8 m o d 53 g^{-m} = 5^{-8} \bmod 53 g − m = 5 − 8 mod 53 . Since 5 8 ≡ 15 ( m o d 53 ) 5^8 \equiv 15 \pmod{53} 5 8 ≡ 15 ( mod 53 ) , we find 15 − 1 ≡ 46 ( m o d 53 ) 15^{-1} \equiv 46 \pmod{53} 1 5 − 1 ≡ 46 ( mod 53 ) (verify: 15 ⋅ 46 = 690 = 13 ⋅ 53 + 1 15 \cdot 46 = 690 = 13 \cdot 53 + 1 15 ⋅ 46 = 690 = 13 ⋅ 53 + 1 ). Now compute h ⋅ ( g − m ) i = 20 ⋅ 46 i m o d 53 h \cdot (g^{-m})^i = 20 \cdot 46^i \bmod 53 h ⋅ ( g − m ) i = 20 ⋅ 4 6 i mod 53 :
i i i 20 ⋅ 46 i m o d 53 20 \cdot 46^i \bmod 53 20 ⋅ 4 6 i mod 53 In table? 0 20 No 1 19 Yes! j = 3 j = 3 j = 3
Result. x = i ⋅ m + j = 1 ⋅ 8 + 3 = 11 x = i \cdot m + j = 1 \cdot 8 + 3 = 11 x = i ⋅ m + j = 1 ⋅ 8 + 3 = 11 .
Verify: 5 11 = 5 8 ⋅ 5 3 = 15 ⋅ 19 = 285 = 5 ⋅ 53 + 20 ≡ 20 ( m o d 53 ) 5^{11} = 5^8 \cdot 5^3 = 15 \cdot 19 = 285 = 5 \cdot 53 + 20 \equiv 20 \pmod{53} 5 11 = 5 8 ⋅ 5 3 = 15 ⋅ 19 = 285 = 5 ⋅ 53 + 20 ≡ 20 ( mod 53 ) . Correct.
We checked just 8 + 1 = 9 8 + 1 = 9 8 + 1 = 9 values instead of scanning all 52. With larger groups, this savings becomes dramatic.
Why It Works Mathematically
Every integer x ∈ [ 0 , p ) x \in [0, p) x ∈ [ 0 , p ) can be uniquely decomposed as x = i m + j x = im + j x = im + j with 0 ≤ j < m 0 \le j < m 0 ≤ j < m and 0 ≤ i < m 0 \le i < m 0 ≤ i < m . So the baby steps cover all possible "remainders mod m m m " and the giant steps cover all possible "quotients by m m m ." The two sets are guaranteed to collide at the right answer.
Complexity
Time : O ( p ) O(\sqrt{p}) O ( p ) — at most p \sqrt{p} p baby steps + p \sqrt{p} p giant steps
Space : O ( p ) O(\sqrt{p}) O ( p ) — storing the baby-step table
This is optimal for generic group algorithms (by Shoup's lower bound), meaning you can't do better without exploiting special group structure.