Skip to content

Commit b31cc12

Browse files
committed
Improve code readability with descriptive names and type hints
- Replace generic parameter names (g, h, p) with descriptive names (base, target, modulus) - Replace generic function name 'f' with 'pseudo_random_function' - Add proper type hints for all parameters and return values - Replace single-letter variables with descriptive names throughout - Maintain backward compatibility with positional arguments - All tests and doctests continue to pass
1 parent 6fda4d5 commit b31cc12

File tree

1 file changed

+60
-36
lines changed

1 file changed

+60
-36
lines changed

maths/pollard_rho_discrete_log.py

Lines changed: 60 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,24 @@
33
import random
44

55

6-
def pollards_rho_discrete_log(g: int, h: int, p: int) -> Optional[int]:
6+
def pollards_rho_discrete_log(base: int, target: int, modulus: int) -> Optional[int]:
77
"""
8-
Solve for x in the discrete logarithm problem: g^x ≡ h (mod p)
8+
Solve for x in the discrete logarithm problem: base^x ≡ target (mod modulus)
99
using Pollard's Rho algorithm.
1010
11-
This is a probabilistic algorithm that finds discrete logarithms in O(√p) time.
11+
This is a probabilistic algorithm that finds discrete logarithms in O(√modulus) time.
1212
The algorithm may not always find a solution in a single run due to its
1313
probabilistic nature, but it will find the correct answer when it succeeds.
1414
1515
More info: https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm_for_logarithms
1616
1717
Parameters
1818
----------
19-
g : int
20-
The generator (base).
21-
h : int
22-
The result value (hg^x mod p).
23-
p : int
19+
base : int
20+
The generator (base of the exponential).
21+
target : int
22+
The target value (targetbase^x mod modulus).
23+
modulus : int
2424
A prime modulus.
2525
2626
Returns
@@ -48,64 +48,88 @@ def pollards_rho_discrete_log(g: int, h: int, p: int) -> Optional[int]:
4848
True
4949
"""
5050

51-
def f(x, a, b):
52-
"""Pseudo-random function that partitions the search space into 3 sets."""
53-
if x % 3 == 0:
54-
# Multiply by g
55-
return (x * g) % p, (a + 1) % (p - 1), b
56-
elif x % 3 == 1:
51+
def pseudo_random_function(
52+
current_value: int, exponent_base: int, exponent_target: int
53+
) -> tuple[int, int, int]:
54+
"""
55+
Pseudo-random function that partitions the search space into 3 sets.
56+
57+
Returns a tuple of (new_value, new_exponent_base, new_exponent_target).
58+
"""
59+
if current_value % 3 == 0:
60+
# Multiply by base
61+
return (
62+
(current_value * base) % modulus,
63+
(exponent_base + 1) % (modulus - 1),
64+
exponent_target,
65+
)
66+
elif current_value % 3 == 1:
5767
# Square
58-
return (x * x) % p, (2 * a) % (p - 1), (2 * b) % (p - 1)
68+
return (
69+
(current_value * current_value) % modulus,
70+
(2 * exponent_base) % (modulus - 1),
71+
(2 * exponent_target) % (modulus - 1),
72+
)
5973
else:
60-
# Multiply by h
61-
return (x * h) % p, a, (b + 1) % (p - 1)
74+
# Multiply by target
75+
return (
76+
(current_value * target) % modulus,
77+
exponent_base,
78+
(exponent_target + 1) % (modulus - 1),
79+
)
6280

6381
# Try multiple random starting points to avoid immediate collisions
6482
max_attempts = 50 # Increased attempts for better reliability
6583

6684
for attempt in range(max_attempts):
6785
# Use different starting values to avoid trivial collisions
68-
# x represents g^a * h^b
86+
# current_value represents base^exponent_base * target^exponent_target
6987
random.seed() # Ensure truly random values
70-
a = random.randint(0, p - 2)
71-
b = random.randint(0, p - 2)
88+
exponent_base = random.randint(0, modulus - 2)
89+
exponent_target = random.randint(0, modulus - 2)
7290

73-
# Ensure x = g^a * h^b mod p
74-
x = (pow(g, a, p) * pow(h, b, p)) % p
91+
# Ensure current_value = base^exponent_base * target^exponent_target mod modulus
92+
current_value = (pow(base, exponent_base, modulus) * pow(target, exponent_target, modulus)) % modulus
7593

76-
# Skip if x is 0 or 1 (problematic starting points)
77-
if x <= 1:
94+
# Skip if current_value is 0 or 1 (problematic starting points)
95+
if current_value <= 1:
7896
continue
7997

80-
X, A, B = x, a, b # Tortoise and hare start at same position
98+
# Tortoise and hare start at same position
99+
tortoise_value, tortoise_exp_base, tortoise_exp_target = current_value, exponent_base, exponent_target
100+
hare_value, hare_exp_base, hare_exp_target = current_value, exponent_base, exponent_target
81101

82102
# Increased iteration limit for better coverage
83-
max_iterations = max(int(math.sqrt(p)) * 2, p // 2)
103+
max_iterations = max(int(math.sqrt(modulus)) * 2, modulus // 2)
84104
for i in range(1, max_iterations):
85105
# Tortoise: one step
86-
x, a, b = f(x, a, b)
106+
tortoise_value, tortoise_exp_base, tortoise_exp_target = pseudo_random_function(
107+
tortoise_value, tortoise_exp_base, tortoise_exp_target
108+
)
87109
# Hare: two steps
88-
X, A, B = f(*f(X, A, B))
110+
hare_value, hare_exp_base, hare_exp_target = pseudo_random_function(
111+
*pseudo_random_function(hare_value, hare_exp_base, hare_exp_target)
112+
)
89113

90-
if x == X and i > 1: # Avoid immediate collision
114+
if tortoise_value == hare_value and i > 1: # Avoid immediate collision
91115
# Collision found
92-
r = (a - A) % (p - 1)
93-
s = (B - b) % (p - 1)
116+
exponent_difference = (tortoise_exp_base - hare_exp_base) % (modulus - 1)
117+
target_difference = (hare_exp_target - tortoise_exp_target) % (modulus - 1)
94118

95-
if s == 0:
119+
if target_difference == 0:
96120
break # Try with different starting point
97121

98122
try:
99123
# Compute modular inverse using extended Euclidean algorithm
100-
inv_s = pow(s, -1, p - 1)
124+
inverse_target_diff = pow(target_difference, -1, modulus - 1)
101125
except ValueError:
102126
break # No inverse, try different starting point
103127

104-
x_log = (r * inv_s) % (p - 1)
128+
discrete_log = (exponent_difference * inverse_target_diff) % (modulus - 1)
105129

106130
# Verify the solution
107-
if pow(g, x_log, p) == h:
108-
return x_log
131+
if pow(base, discrete_log, modulus) == target:
132+
return discrete_log
109133
break # This attempt failed, try with different starting point
110134

111135
return None

0 commit comments

Comments
 (0)