1+ """
2+ Pollard's Rho Algorithm for Discrete Logarithm
3+
4+ Solves the discrete logarithm problem: Find x such that g^x ≡ h (mod p), where g is a generator, h is the target,
5+ and p is prime. Uses Pollard's Rho method with random walks and Brent's cycle detection.
6+
7+ Examples:
8+ Example:
9+ pollards_rho_discrete_log(2, 22, 29)
10+ 11
11+ (Since 2^11 % 29 = 22)
12+
13+ Constraints:
14+ - p should be prime, 3 <= p <= 10^6 (for reasonable runtime).
15+ - g should be a primitive root modulo p (or at least a generator).
16+ - Algorithm is probabilistic; may require multiple runs for success.
17+
18+ Implementation: Random walks in group with collision detection.
19+ Time Complexity: O(√p) expected
20+ Space Complexity: O(1)
21+ """
22+
23+ import random
24+ from typing import Optional
25+
26+
27+ def pollards_rho_discrete_log (g : int , h : int , p : int ) -> Optional [int ]:
28+ """
29+ Returns x such that g^x ≡ h (mod p), using Pollard's Rho discrete logarithm algorithm.
30+
31+ Args:
32+ g (int): Base (generator).
33+ h (int): Target value.
34+ p (int): Modulus (prime).
35+
36+ Returns:
37+ int or None: The discrete logarithm x if found, else None (retry with different parameters).
38+ """
39+ if p < 3 or h == 0 :
40+ raise ValueError ("Invalid input: p must be prime >= 3, h != 0" )
41+
42+ # Function to compute discrete log candidate using baby-step giant-step on subgroup if needed
43+ # But for full Rho, we use tortoise-hare
44+ def f (x : int , y : int ) -> int :
45+ """Random walk function: x * g^y mod p (simplified Brent variant)."""
46+ return (x * pow (g , y , p )) % p
47+
48+ # Initial values (randomize for retries)
49+ x0 = random .randint (1 , p - 1 )
50+ y0 = random .randint (0 , p - 1 )
51+ tortoise = (x0 , y0 )
52+ hare = (x0 , y0 )
53+ mu = 0 # Cycle start
54+
55+ # Brent's cycle detection
56+ power = 1
57+ lam = 1
58+ while True :
59+ # Move tortoise one step
60+ tortoise = (f (tortoise [0 ], tortoise [1 ]), tortoise [1 ] + 1 )
61+ if tortoise [0 ] == h :
62+ return tortoise [1 ] % (p - 1 ) # x mod phi(p)
63+
64+ # Move hare 2^power steps
65+ for _ in range (power ):
66+ hare = (f (hare [0 ], hare [1 ]), hare [1 ] + 1 )
67+ if hare [0 ] == h :
68+ return hare [1 ] % (p - 1 )
69+
70+ if tortoise [0 ] == hare [0 ]:
71+ # Collision detected
72+ if tortoise [1 ] == hare [1 ]:
73+ # Same point, restart
74+ x0 = random .randint (1 , p - 1 )
75+ y0 = random .randint (0 , p - 1 )
76+ tortoise = (x0 , y0 )
77+ hare = (x0 , y0 )
78+ continue
79+
80+ # Compute mu and lam for cycle
81+ mu = 0
82+ tortoise2 = (x0 , y0 )
83+ while tortoise2 != tortoise :
84+ tortoise2 = (f (tortoise2 [0 ], tortoise2 [1 ]), tortoise2 [1 ] + 1 )
85+ mu += 1
86+
87+ lam = 1
88+ hare2 = tortoise
89+ while hare2 != tortoise :
90+ hare2 = (f (hare2 [0 ], hare2 [1 ]), hare2 [1 ] + 1 )
91+ tortoise = (f (tortoise [0 ], tortoise [1 ]), tortoise [1 ] + 1 )
92+ lam += 1
93+
94+ # Now solve for x using collision
95+ # Since collision at x * g^y = h, but for DLP, we need to adjust
96+ # This is simplified; for full DLP, use baby-step on subgroup or BSGS hybrid
97+ # For this implementation, assume collision gives candidate x = (hare[1] - tortoise[1]) mod (p-1)
98+ candidate_x = (hare [1 ] - tortoise [1 ]) % (p - 1 )
99+ if pow (g , candidate_x , p ) == h :
100+ return candidate_x
101+
102+ # If not, retry with new random
103+ x0 = random .randint (1 , p - 1 )
104+ y0 = random .randint (0 , p - 1 )
105+ tortoise = (x0 , y0 )
106+ hare = (x0 , y0 )
107+
108+ power *= 2
109+ if power > p : # Prevent infinite loop
110+ return None
111+
112+ return None # Fallback
113+
114+
115+ # Optional: Simple tests
116+ if __name__ == "__main__" :
117+ # Test with example: 2^11 % 29 = 22
118+ result = pollards_rho_discrete_log (2 , 22 , 29 )
119+ assert result == 11 , f"Expected 11, got { result } "
120+ print (f"Test passed: { result } " )
121+
122+ # Another test: 5^3 % 13 = 12 (5^3 = 125 % 13 = 12)
123+ result2 = pollards_rho_discrete_log (5 , 12 , 13 )
124+ assert result2 == 3 , f"Expected 3, got { result2 } "
125+ print (f"Test passed: { result2 } " )
0 commit comments