-
-
Notifications
You must be signed in to change notification settings - Fork 50.1k
Add Pollard's Rho for discrete logarithm #13656 #13743
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| """ | ||
| Pollard's Rho Algorithm for Discrete Logarithm | ||
|
|
||
| Solves the discrete logarithm problem: Find x such that g^x ≡ h (mod p), where g is a generator, h is the target, | ||
| and p is prime. Uses Pollard's Rho method with random walks and Brent's cycle detection. | ||
|
|
||
| Examples: | ||
| Example: | ||
| pollards_rho_discrete_log(2, 22, 29) | ||
| 11 | ||
| (Since 2^11 % 29 = 22) | ||
|
|
||
| Constraints: | ||
| - p should be prime, 3 <= p <= 10^6 (for reasonable runtime). | ||
| - g should be a primitive root modulo p (or at least a generator). | ||
| - Algorithm is probabilistic; may require multiple runs for success. | ||
|
|
||
| Implementation: Random walks in group with collision detection. | ||
| Time Complexity: O(√p) expected | ||
| Space Complexity: O(1) | ||
| """ | ||
|
|
||
| import random | ||
| from typing import Optional | ||
|
|
||
|
|
||
| def pollards_rho_discrete_log(g: int, h: int, p: int) -> Optional[int]: | ||
| """ | ||
| Returns x such that g^x ≡ h (mod p), using Pollard's Rho discrete logarithm algorithm. | ||
|
|
||
| Args: | ||
| g (int): Base (generator). | ||
| h (int): Target value. | ||
| p (int): Modulus (prime). | ||
|
|
||
| Returns: | ||
| int or None: The discrete logarithm x if found, else None (retry with different parameters). | ||
| """ | ||
| if p < 3 or h == 0: | ||
| raise ValueError("Invalid input: p must be prime >= 3, h != 0") | ||
|
|
||
| # Function to compute discrete log candidate using baby-step giant-step on subgroup if needed | ||
| # But for full Rho, we use tortoise-hare | ||
| def f(x: int, y: int) -> int: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please provide descriptive name for the function: As there is no test file in this pull request nor any test function or class in the file Please provide descriptive name for the parameter: Please provide descriptive name for the parameter: |
||
| """Random walk function: x * g^y mod p (simplified Brent variant).""" | ||
| return (x * pow(g, y, p)) % p | ||
|
|
||
| # Initial values (randomize for retries) | ||
| x0 = random.randint(1, p - 1) | ||
| y0 = random.randint(0, p - 1) | ||
| tortoise = (x0, y0) | ||
| hare = (x0, y0) | ||
| mu = 0 # Cycle start | ||
|
|
||
| # Brent's cycle detection | ||
| power = 1 | ||
| lam = 1 | ||
| while True: | ||
| # Move tortoise one step | ||
| tortoise = (f(tortoise[0], tortoise[1]), tortoise[1] + 1) | ||
| if tortoise[0] == h: | ||
| return tortoise[1] % (p - 1) # x mod phi(p) | ||
|
|
||
| # Move hare 2^power steps | ||
| for _ in range(power): | ||
| hare = (f(hare[0], hare[1]), hare[1] + 1) | ||
| if hare[0] == h: | ||
| return hare[1] % (p - 1) | ||
|
|
||
| if tortoise[0] == hare[0]: | ||
| # Collision detected | ||
| if tortoise[1] == hare[1]: | ||
| # Same point, restart | ||
| x0 = random.randint(1, p - 1) | ||
| y0 = random.randint(0, p - 1) | ||
| tortoise = (x0, y0) | ||
| hare = (x0, y0) | ||
| continue | ||
|
|
||
| # Compute mu and lam for cycle | ||
| mu = 0 | ||
| tortoise2 = (x0, y0) | ||
| while tortoise2 != tortoise: | ||
| tortoise2 = (f(tortoise2[0], tortoise2[1]), tortoise2[1] + 1) | ||
| mu += 1 | ||
|
|
||
| lam = 1 | ||
| hare2 = tortoise | ||
| while hare2 != tortoise: | ||
| hare2 = (f(hare2[0], hare2[1]), hare2[1] + 1) | ||
| tortoise = (f(tortoise[0], tortoise[1]), tortoise[1] + 1) | ||
| lam += 1 | ||
|
|
||
| # Now solve for x using collision | ||
| # Since collision at x * g^y = h, but for DLP, we need to adjust | ||
| # This is simplified; for full DLP, use baby-step on subgroup or BSGS hybrid | ||
| # For this implementation, assume collision gives candidate x = (hare[1] - tortoise[1]) mod (p-1) | ||
| candidate_x = (hare[1] - tortoise[1]) % (p - 1) | ||
| if pow(g, candidate_x, p) == h: | ||
| return candidate_x | ||
|
|
||
| # If not, retry with new random | ||
| x0 = random.randint(1, p - 1) | ||
| y0 = random.randint(0, p - 1) | ||
| tortoise = (x0, y0) | ||
| hare = (x0, y0) | ||
|
|
||
| power *= 2 | ||
| if power > p: # Prevent infinite loop | ||
| return None | ||
|
|
||
| return None # Fallback | ||
|
|
||
|
|
||
| # Optional: Simple tests | ||
| if __name__ == "__main__": | ||
| # Test with example: 2^11 % 29 = 22 | ||
| result = pollards_rho_discrete_log(2, 22, 29) | ||
| assert result == 11, f"Expected 11, got {result}" | ||
| print(f"Test passed: {result}") | ||
|
|
||
| # Another test: 5^3 % 13 = 12 (5^3 = 125 % 13 = 12) | ||
| result2 = pollards_rho_discrete_log(5, 12, 13) | ||
| assert result2 == 3, f"Expected 3, got {result2}" | ||
| print(f"Test passed: {result2}") | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As there is no test file in this pull request nor any test function or class in the file
maths/pollard_rho_discrete_log.py, please provide doctest for the functionpollards_rho_discrete_logPlease provide descriptive name for the parameter:
gPlease provide descriptive name for the parameter:
hPlease provide descriptive name for the parameter:
p