Skip to content

Commit ecf5bc1

Browse files
tianyizheng02github-actions
authored andcommitted
Consolidate binary exponentiation files (TheAlgorithms#10742)
* 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>
1 parent 4f9092c commit ecf5bc1

File tree

4 files changed

+181
-124
lines changed

4 files changed

+181
-124
lines changed

Diff for: DIRECTORY.md

-2
Original file line numberDiff line numberDiff line change
@@ -578,9 +578,7 @@
578578
* [Bailey Borwein Plouffe](maths/bailey_borwein_plouffe.py)
579579
* [Base Neg2 Conversion](maths/base_neg2_conversion.py)
580580
* [Basic Maths](maths/basic_maths.py)
581-
* [Binary Exp Mod](maths/binary_exp_mod.py)
582581
* [Binary Exponentiation](maths/binary_exponentiation.py)
583-
* [Binary Exponentiation 2](maths/binary_exponentiation_2.py)
584582
* [Binary Multiplication](maths/binary_multiplication.py)
585583
* [Binomial Coefficient](maths/binomial_coefficient.py)
586584
* [Binomial Distribution](maths/binomial_distribution.py)

Diff for: maths/binary_exp_mod.py

-28
This file was deleted.

Diff for: maths/binary_exponentiation.py

+181-33
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,196 @@
1-
"""Binary Exponentiation."""
1+
"""
2+
Binary Exponentiation
23
3-
# Author : Junth Basnet
4-
# Time Complexity : O(logn)
4+
This is a method to find a^b in O(log b) time complexity and is one of the most commonly
5+
used methods of exponentiation. The method is also useful for modular exponentiation,
6+
when the solution to (a^b) % c is required.
57
8+
To calculate a^b:
9+
- If b is even, then a^b = (a * a)^(b / 2)
10+
- If b is odd, then a^b = a * a^(b - 1)
11+
Repeat until b = 1 or b = 0
612
7-
def binary_exponentiation(a: int, n: int) -> int:
13+
For modular exponentiation, we use the fact that (a * b) % c = ((a % c) * (b % c)) % c
14+
"""
15+
16+
17+
def binary_exp_recursive(base: float, exponent: int) -> float:
818
"""
9-
Compute a number raised by some quantity
10-
>>> binary_exponentiation(-1, 3)
19+
Computes a^b recursively, where a is the base and b is the exponent
20+
21+
>>> binary_exp_recursive(3, 5)
22+
243
23+
>>> binary_exp_recursive(11, 13)
24+
34522712143931
25+
>>> binary_exp_recursive(-1, 3)
1126
-1
12-
>>> binary_exponentiation(-1, 4)
27+
>>> binary_exp_recursive(0, 5)
28+
0
29+
>>> binary_exp_recursive(3, 1)
30+
3
31+
>>> binary_exp_recursive(3, 0)
1332
1
14-
>>> binary_exponentiation(2, 2)
15-
4
16-
>>> binary_exponentiation(3, 5)
33+
>>> binary_exp_recursive(1.5, 4)
34+
5.0625
35+
>>> binary_exp_recursive(3, -1)
36+
Traceback (most recent call last):
37+
...
38+
ValueError: Exponent must be a non-negative integer
39+
"""
40+
if exponent < 0:
41+
raise ValueError("Exponent must be a non-negative integer")
42+
43+
if exponent == 0:
44+
return 1
45+
46+
if exponent % 2 == 1:
47+
return binary_exp_recursive(base, exponent - 1) * base
48+
49+
b = binary_exp_recursive(base, exponent // 2)
50+
return b * b
51+
52+
53+
def binary_exp_iterative(base: float, exponent: int) -> float:
54+
"""
55+
Computes a^b iteratively, where a is the base and b is the exponent
56+
57+
>>> binary_exp_iterative(3, 5)
1758
243
18-
>>> binary_exponentiation(10, 3)
19-
1000
20-
>>> binary_exponentiation(5e3, 1)
21-
5000.0
22-
>>> binary_exponentiation(-5e3, 1)
23-
-5000.0
24-
"""
25-
if n == 0:
59+
>>> binary_exp_iterative(11, 13)
60+
34522712143931
61+
>>> binary_exp_iterative(-1, 3)
62+
-1
63+
>>> binary_exp_iterative(0, 5)
64+
0
65+
>>> binary_exp_iterative(3, 1)
66+
3
67+
>>> binary_exp_iterative(3, 0)
68+
1
69+
>>> binary_exp_iterative(1.5, 4)
70+
5.0625
71+
>>> binary_exp_iterative(3, -1)
72+
Traceback (most recent call last):
73+
...
74+
ValueError: Exponent must be a non-negative integer
75+
"""
76+
if exponent < 0:
77+
raise ValueError("Exponent must be a non-negative integer")
78+
79+
res: int | float = 1
80+
while exponent > 0:
81+
if exponent & 1:
82+
res *= base
83+
84+
base *= base
85+
exponent >>= 1
86+
87+
return res
88+
89+
90+
def binary_exp_mod_recursive(base: float, exponent: int, modulus: int) -> float:
91+
"""
92+
Computes a^b % c recursively, where a is the base, b is the exponent, and c is the
93+
modulus
94+
95+
>>> binary_exp_mod_recursive(3, 4, 5)
96+
1
97+
>>> binary_exp_mod_recursive(11, 13, 7)
98+
4
99+
>>> binary_exp_mod_recursive(1.5, 4, 3)
100+
2.0625
101+
>>> binary_exp_mod_recursive(7, -1, 10)
102+
Traceback (most recent call last):
103+
...
104+
ValueError: Exponent must be a non-negative integer
105+
>>> binary_exp_mod_recursive(7, 13, 0)
106+
Traceback (most recent call last):
107+
...
108+
ValueError: Modulus must be a positive integer
109+
"""
110+
if exponent < 0:
111+
raise ValueError("Exponent must be a non-negative integer")
112+
if modulus <= 0:
113+
raise ValueError("Modulus must be a positive integer")
114+
115+
if exponent == 0:
26116
return 1
27117

28-
elif n % 2 == 1:
29-
return binary_exponentiation(a, n - 1) * a
118+
if exponent % 2 == 1:
119+
return (binary_exp_mod_recursive(base, exponent - 1, modulus) * base) % modulus
30120

31-
else:
32-
b = binary_exponentiation(a, n // 2)
33-
return b * b
121+
r = binary_exp_mod_recursive(base, exponent // 2, modulus)
122+
return (r * r) % modulus
34123

35124

36-
if __name__ == "__main__":
37-
import doctest
125+
def binary_exp_mod_iterative(base: float, exponent: int, modulus: int) -> float:
126+
"""
127+
Computes a^b % c iteratively, where a is the base, b is the exponent, and c is the
128+
modulus
38129
39-
doctest.testmod()
130+
>>> binary_exp_mod_iterative(3, 4, 5)
131+
1
132+
>>> binary_exp_mod_iterative(11, 13, 7)
133+
4
134+
>>> binary_exp_mod_iterative(1.5, 4, 3)
135+
2.0625
136+
>>> binary_exp_mod_iterative(7, -1, 10)
137+
Traceback (most recent call last):
138+
...
139+
ValueError: Exponent must be a non-negative integer
140+
>>> binary_exp_mod_iterative(7, 13, 0)
141+
Traceback (most recent call last):
142+
...
143+
ValueError: Modulus must be a positive integer
144+
"""
145+
if exponent < 0:
146+
raise ValueError("Exponent must be a non-negative integer")
147+
if modulus <= 0:
148+
raise ValueError("Modulus must be a positive integer")
149+
150+
res: int | float = 1
151+
while exponent > 0:
152+
if exponent & 1:
153+
res = ((res % modulus) * (base % modulus)) % modulus
154+
155+
base *= base
156+
exponent >>= 1
157+
158+
return res
159+
160+
161+
if __name__ == "__main__":
162+
from timeit import timeit
40163

41-
try:
42-
BASE = int(float(input("Enter Base : ").strip()))
43-
POWER = int(input("Enter Power : ").strip())
44-
except ValueError:
45-
print("Invalid literal for integer")
164+
a = 1269380576
165+
b = 374
166+
c = 34
46167

47-
RESULT = binary_exponentiation(BASE, POWER)
48-
print(f"{BASE}^({POWER}) : {RESULT}")
168+
runs = 100_000
169+
print(
170+
timeit(
171+
f"binary_exp_recursive({a}, {b})",
172+
setup="from __main__ import binary_exp_recursive",
173+
number=runs,
174+
)
175+
)
176+
print(
177+
timeit(
178+
f"binary_exp_iterative({a}, {b})",
179+
setup="from __main__ import binary_exp_iterative",
180+
number=runs,
181+
)
182+
)
183+
print(
184+
timeit(
185+
f"binary_exp_mod_recursive({a}, {b}, {c})",
186+
setup="from __main__ import binary_exp_mod_recursive",
187+
number=runs,
188+
)
189+
)
190+
print(
191+
timeit(
192+
f"binary_exp_mod_iterative({a}, {b}, {c})",
193+
setup="from __main__ import binary_exp_mod_iterative",
194+
number=runs,
195+
)
196+
)

Diff for: maths/binary_exponentiation_2.py

-61
This file was deleted.

0 commit comments

Comments
 (0)