|
1 |
| -"""Binary Exponentiation.""" |
| 1 | +""" |
| 2 | +Binary Exponentiation |
2 | 3 |
|
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. |
5 | 7 |
|
| 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 |
6 | 12 |
|
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: |
8 | 18 | """
|
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) |
11 | 26 | -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) |
13 | 32 | 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) |
17 | 58 | 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: |
26 | 116 | return 1
|
27 | 117 |
|
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 |
30 | 120 |
|
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 |
34 | 123 |
|
35 | 124 |
|
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 |
38 | 129 |
|
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 |
40 | 163 |
|
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 |
46 | 167 |
|
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 | + ) |
0 commit comments