Skip to content
68 changes: 68 additions & 0 deletions maths/binary_exp_mod.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,74 @@ def bin_exp_mod(a, n, b):
return (r * r) % b


def binary_exponentiation_mod_multiplication(a, b, c):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function also appears to calculate (modular) multiplication rather than exponentiation, so I feel like this might also belong better in a separate file for binary multiplication

"""
* Binary Exponentiation with Multiplication
* This is a method to find a*b in a time complexity of O(log b)
* This is one of the most commonly used methods of finding result of multiplication.
* Also useful in cases where solution to (a*b)%c is required,
* where a,b,c can be numbers over the computers calculation limits.
* Done using iteration, can also be done using recursion

* Let's say you need to calculate a ^ b
* RULE 1 : a * b = (a+a) * (b/2) -- example : 4 * 4 = (4+4) * (4/2) = 8 * 2
* RULE 2 : IF b is ODD, then -- a * b = a + (a * (b - 1)) :: where (b - 1) is even.
* Once b is even, repeat the process to get a * b
* Repeat the process till b = 1 OR b = 0, because a*1 = a AND a*0 = 0
*
* As far as the modulo is concerned,
* the fact : (a+b) % c = ((a%c) + (b%c)) % c
* Now apply RULE 1 OR 2, whichever is required.

* @author chinmoy159
* @version 1.0 dated 10/08/2017
"""

res = 0
while b > 0:
if b & 1:
res = ((res % c) + (a % c)) % c

a += a
b >>= 1

return res


def binary_exponentiation_mod_powers(a, b, c):
"""
* Binary Exponentiation for Powers
* This is a method to find a^b in a time complexity of O(log b)
* This is one of the most commonly used methods of finding powers.
* Also useful in cases where solution to (a^b)%c is required,
* where a,b,c can be numbers over the computers calculation limits.
* Done using iteration, can also be done using recursion

* Let's say you need to calculate a ^ b
* RULE 1 : a ^ b = (a*a) ^ (b/2) -- example : 4 ^ 4 = (4*4) ^ (4/2) = 16 ^ 2
* RULE 2 : IF b is ODD, then -- a ^ b = a * (a ^ (b - 1)) :: where (b - 1) is even.
* Once b is even, repeat the process to get a ^ b
* Repeat the process till b = 1 OR b = 0, because a^1 = a AND a^0 = 1
*
* As far as the modulo is concerned,
* the fact : (a*b) % c = ((a%c) * (b%c)) % c
* Now apply RULE 1 OR 2 whichever is required.

* @author chinmoy159
* @version 1.0 dated 10/08/2017
"""

res = 1
while b > 0:
if b & 1:
res = ((res % c) * (a % c)) % c

a *= a
b >>= 1

return res


if __name__ == "__main__":
try:
BASE = int(input("Enter Base : ").strip())
Expand Down
78 changes: 68 additions & 10 deletions maths/binary_exponentiation.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,78 @@
"""Binary Exponentiation."""
def binary_exponentiation_multiplication(a, b):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this function calculates a * b rather than a ** b, so shouldn't it belong in a file like binary_multiplication.py instead?

"""
* Binary Exponentiation with Multiplication
* This is a method to find a*b in a time complexity of O(log b)
* This is one of the most commonly used methods of finding result of multiplication.
* Also useful in cases where solution to (a*b)%c is required,
* where a,b,c can be numbers over the computers calculation limits.
* Done using iteration, can also be done using recursion

# Author : Junth Basnet
# Time Complexity : O(logn)
* Let's say you need to calculate a * b
* RULE 1 : a * b = (a+a) * (b/2) -- example : 4 * 4 = (4+4) * (4/2) = 8 * 2
* RULE 2 : IF b is ODD, then -- a * b = a + (a * (b - 1)) :: where (b - 1) is even.
* Once b is even, repeat the process to get a * b
* Repeat the process till b = 1 OR b = 0, because a*1 = a AND a*0 = 0

* @author chinmoy159
* @version 1.0 dated 10/08/2017
"""

def binary_exponentiation(a, n):
res = 0
while b > 0:
if b & 1:
res += a

if n == 0:
a += a
b >>= 1

return res


def binary_exponentiation_powers(a, b):
"""
* Binary Exponentiation for Powers
* This is a method to find a^b in a time complexity of O(log b)
* This is one of the most commonly used methods of finding powers.
* Also useful in cases where solution to (a^b)%c is required,
* where a,b,c can be numbers over the computers calculation limits.
* Done using iteration, can also be done using recursion

* Let's say you need to calculate a ^ b
* RULE 1 : a ^ b = (a*a) ^ (b/2) -- example : 4 ^ 4 = (4*4) ^ (4/2) = 16 ^ 2
* RULE 2 : IF b is ODD, then -- a ^ b = a * (a ^ (b - 1)) :: where (b - 1) is even.
* Once b is even, repeat the process to get a ^ b
* Repeat the process till b = 1 OR b = 0, because a^1 = a AND a^0 = 1

* @author chinmoy159
* @version 1.0 dated 10/08/2017
"""

res = 1
while b > 0:
if b & 1:
res *= a

a *= a
b >>= 1

return res


def binary_exponentiation_recursion(a, b):
"""Binary Exponentiation with recursion.

* Time Complexity : O(logn)
* @author : Junth Basnet
"""

if b == 0:
return 1

elif n % 2 == 1:
return binary_exponentiation(a, n - 1) * a
elif b % 2 == 1:
return binary_exponentiation_recursion(a, b - 1) * a

else:
b = binary_exponentiation(a, n / 2)
return b * b
return binary_exponentiation_recursion(a, b / 2) ** 2


if __name__ == "__main__":
Expand All @@ -24,5 +82,5 @@ def binary_exponentiation(a, n):
except ValueError:
print("Invalid literal for integer")

RESULT = binary_exponentiation(BASE, POWER)
RESULT = binary_exponentiation_recursion(BASE, POWER)
print(f"{BASE}^({POWER}) : {RESULT}")
50 changes: 0 additions & 50 deletions maths/binary_exponentiation_2.py

This file was deleted.

50 changes: 0 additions & 50 deletions maths/binary_exponentiation_3.py

This file was deleted.

33 changes: 0 additions & 33 deletions maths/double_factorial_iterative.py

This file was deleted.

33 changes: 31 additions & 2 deletions maths/double_factorial_recursive.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,33 @@
def double_factorial(n: int) -> int:
def double_factorial(num: int) -> int:
"""
Compute double factorial using iterative method.

To learn about the theory behind this algorithm:
https://en.wikipedia.org/wiki/Double_factorial

>>> import math
>>> all(double_factorial(i) == math.prod(range(i, 0, -2)) for i in range(20))
True
>>> double_factorial(0.1)
Traceback (most recent call last):
...
ValueError: double_factorial() only accepts integral values
>>> double_factorial(-1)
Traceback (most recent call last):
...
ValueError: double_factorial() not defined for negative values
"""
if not isinstance(num, int):
raise ValueError("double_factorial() only accepts integral values")
if num < 0:
raise ValueError("double_factorial() not defined for negative values")
value = 1
for i in range(num, 0, -2):
value *= i
return value


def double_factorial_recursive(n: int) -> int:
"""
Compute double factorial using recursive method.
Recursion can be costly for large numbers.
Expand All @@ -22,7 +51,7 @@ def double_factorial(n: int) -> int:
raise ValueError("double_factorial() only accepts integral values")
if n < 0:
raise ValueError("double_factorial() not defined for negative values")
return 1 if n <= 1 else n * double_factorial(n - 2)
return 1 if n <= 1 else n * double_factorial_recursive(n - 2)


if __name__ == "__main__":
Expand Down
Loading