mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-11-27 15:01:08 +00:00
06edc0eea0
* Consolidate binary exponentiation files * updating DIRECTORY.md * Fix typos in doctests * Add suggestions from code review * Fix timeit benchmarks --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
197 lines
5.1 KiB
Python
197 lines
5.1 KiB
Python
"""
|
|
Binary Exponentiation
|
|
|
|
This is a method to find a^b in O(log b) time complexity and is one of the most commonly
|
|
used methods of exponentiation. The method is also useful for modular exponentiation,
|
|
when the solution to (a^b) % c is required.
|
|
|
|
To calculate a^b:
|
|
- If b is even, then a^b = (a * a)^(b / 2)
|
|
- If b is odd, then a^b = a * a^(b - 1)
|
|
Repeat until b = 1 or b = 0
|
|
|
|
For modular exponentiation, we use the fact that (a * b) % c = ((a % c) * (b % c)) % c
|
|
"""
|
|
|
|
|
|
def binary_exp_recursive(base: float, exponent: int) -> float:
|
|
"""
|
|
Computes a^b recursively, where a is the base and b is the exponent
|
|
|
|
>>> binary_exp_recursive(3, 5)
|
|
243
|
|
>>> binary_exp_recursive(11, 13)
|
|
34522712143931
|
|
>>> binary_exp_recursive(-1, 3)
|
|
-1
|
|
>>> binary_exp_recursive(0, 5)
|
|
0
|
|
>>> binary_exp_recursive(3, 1)
|
|
3
|
|
>>> binary_exp_recursive(3, 0)
|
|
1
|
|
>>> binary_exp_recursive(1.5, 4)
|
|
5.0625
|
|
>>> binary_exp_recursive(3, -1)
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: Exponent must be a non-negative integer
|
|
"""
|
|
if exponent < 0:
|
|
raise ValueError("Exponent must be a non-negative integer")
|
|
|
|
if exponent == 0:
|
|
return 1
|
|
|
|
if exponent % 2 == 1:
|
|
return binary_exp_recursive(base, exponent - 1) * base
|
|
|
|
b = binary_exp_recursive(base, exponent // 2)
|
|
return b * b
|
|
|
|
|
|
def binary_exp_iterative(base: float, exponent: int) -> float:
|
|
"""
|
|
Computes a^b iteratively, where a is the base and b is the exponent
|
|
|
|
>>> binary_exp_iterative(3, 5)
|
|
243
|
|
>>> binary_exp_iterative(11, 13)
|
|
34522712143931
|
|
>>> binary_exp_iterative(-1, 3)
|
|
-1
|
|
>>> binary_exp_iterative(0, 5)
|
|
0
|
|
>>> binary_exp_iterative(3, 1)
|
|
3
|
|
>>> binary_exp_iterative(3, 0)
|
|
1
|
|
>>> binary_exp_iterative(1.5, 4)
|
|
5.0625
|
|
>>> binary_exp_iterative(3, -1)
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: Exponent must be a non-negative integer
|
|
"""
|
|
if exponent < 0:
|
|
raise ValueError("Exponent must be a non-negative integer")
|
|
|
|
res: int | float = 1
|
|
while exponent > 0:
|
|
if exponent & 1:
|
|
res *= base
|
|
|
|
base *= base
|
|
exponent >>= 1
|
|
|
|
return res
|
|
|
|
|
|
def binary_exp_mod_recursive(base: float, exponent: int, modulus: int) -> float:
|
|
"""
|
|
Computes a^b % c recursively, where a is the base, b is the exponent, and c is the
|
|
modulus
|
|
|
|
>>> binary_exp_mod_recursive(3, 4, 5)
|
|
1
|
|
>>> binary_exp_mod_recursive(11, 13, 7)
|
|
4
|
|
>>> binary_exp_mod_recursive(1.5, 4, 3)
|
|
2.0625
|
|
>>> binary_exp_mod_recursive(7, -1, 10)
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: Exponent must be a non-negative integer
|
|
>>> binary_exp_mod_recursive(7, 13, 0)
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: Modulus must be a positive integer
|
|
"""
|
|
if exponent < 0:
|
|
raise ValueError("Exponent must be a non-negative integer")
|
|
if modulus <= 0:
|
|
raise ValueError("Modulus must be a positive integer")
|
|
|
|
if exponent == 0:
|
|
return 1
|
|
|
|
if exponent % 2 == 1:
|
|
return (binary_exp_mod_recursive(base, exponent - 1, modulus) * base) % modulus
|
|
|
|
r = binary_exp_mod_recursive(base, exponent // 2, modulus)
|
|
return (r * r) % modulus
|
|
|
|
|
|
def binary_exp_mod_iterative(base: float, exponent: int, modulus: int) -> float:
|
|
"""
|
|
Computes a^b % c iteratively, where a is the base, b is the exponent, and c is the
|
|
modulus
|
|
|
|
>>> binary_exp_mod_iterative(3, 4, 5)
|
|
1
|
|
>>> binary_exp_mod_iterative(11, 13, 7)
|
|
4
|
|
>>> binary_exp_mod_iterative(1.5, 4, 3)
|
|
2.0625
|
|
>>> binary_exp_mod_iterative(7, -1, 10)
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: Exponent must be a non-negative integer
|
|
>>> binary_exp_mod_iterative(7, 13, 0)
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: Modulus must be a positive integer
|
|
"""
|
|
if exponent < 0:
|
|
raise ValueError("Exponent must be a non-negative integer")
|
|
if modulus <= 0:
|
|
raise ValueError("Modulus must be a positive integer")
|
|
|
|
res: int | float = 1
|
|
while exponent > 0:
|
|
if exponent & 1:
|
|
res = ((res % modulus) * (base % modulus)) % modulus
|
|
|
|
base *= base
|
|
exponent >>= 1
|
|
|
|
return res
|
|
|
|
|
|
if __name__ == "__main__":
|
|
from timeit import timeit
|
|
|
|
a = 1269380576
|
|
b = 374
|
|
c = 34
|
|
|
|
runs = 100_000
|
|
print(
|
|
timeit(
|
|
f"binary_exp_recursive({a}, {b})",
|
|
setup="from __main__ import binary_exp_recursive",
|
|
number=runs,
|
|
)
|
|
)
|
|
print(
|
|
timeit(
|
|
f"binary_exp_iterative({a}, {b})",
|
|
setup="from __main__ import binary_exp_iterative",
|
|
number=runs,
|
|
)
|
|
)
|
|
print(
|
|
timeit(
|
|
f"binary_exp_mod_recursive({a}, {b}, {c})",
|
|
setup="from __main__ import binary_exp_mod_recursive",
|
|
number=runs,
|
|
)
|
|
)
|
|
print(
|
|
timeit(
|
|
f"binary_exp_mod_iterative({a}, {b}, {c})",
|
|
setup="from __main__ import binary_exp_mod_iterative",
|
|
number=runs,
|
|
)
|
|
)
|