From b7e876a7ec1de64c4823da2d711ef9538cd49c05 Mon Sep 17 00:00:00 2001 From: CaedenPH Date: Wed, 25 Jan 2023 19:27:25 +0000 Subject: [PATCH 01/10] feat: Concatenate both factorial implementations --- .../{factorial_iterative.py => factorial.py} | 24 ++++++++++++++++ maths/factorial_recursive.py | 28 ------------------- 2 files changed, 24 insertions(+), 28 deletions(-) rename maths/{factorial_iterative.py => factorial.py} (59%) delete mode 100644 maths/factorial_recursive.py diff --git a/maths/factorial_iterative.py b/maths/factorial.py similarity index 59% rename from maths/factorial_iterative.py rename to maths/factorial.py index c6cf7de57ab2..3aa5821813ae 100644 --- a/maths/factorial_iterative.py +++ b/maths/factorial.py @@ -34,6 +34,30 @@ def factorial(number: int) -> int: return value +def factorial(n: int) -> int: + """ + Calculate the factorial of a positive integer + https://en.wikipedia.org/wiki/Factorial + + >>> import math + >>> all(factorial(i) == math.factorial(i) for i in range(20)) + True + >>> factorial(0.1) + Traceback (most recent call last): + ... + ValueError: factorial() only accepts integral values + >>> factorial(-1) + Traceback (most recent call last): + ... + ValueError: factorial() not defined for negative values + """ + if not isinstance(n, int): + raise ValueError("factorial() only accepts integral values") + if n < 0: + raise ValueError("factorial() not defined for negative values") + return 1 if n == 0 or n == 1 else n * factorial(n - 1) + + if __name__ == "__main__": import doctest diff --git a/maths/factorial_recursive.py b/maths/factorial_recursive.py deleted file mode 100644 index 137112738905..000000000000 --- a/maths/factorial_recursive.py +++ /dev/null @@ -1,28 +0,0 @@ -def factorial(n: int) -> int: - """ - Calculate the factorial of a positive integer - https://en.wikipedia.org/wiki/Factorial - - >>> import math - >>> all(factorial(i) == math.factorial(i) for i in range(20)) - True - >>> factorial(0.1) - Traceback (most recent call last): - ... - ValueError: factorial() only accepts integral values - >>> factorial(-1) - Traceback (most recent call last): - ... - ValueError: factorial() not defined for negative values - """ - if not isinstance(n, int): - raise ValueError("factorial() only accepts integral values") - if n < 0: - raise ValueError("factorial() not defined for negative values") - return 1 if n == 0 or n == 1 else n * factorial(n - 1) - - -if __name__ == "__main__": - import doctest - - doctest.testmod() From 0b8f54ae2b44f260a1a325b0b5c9c516ddebb196 Mon Sep 17 00:00:00 2001 From: CaedenPH Date: Wed, 25 Jan 2023 19:34:07 +0000 Subject: [PATCH 02/10] feat: Concatenate algorithm implementations --- maths/find_max.py | 51 ++++++++++++++++++++++++ maths/find_max_recursion.py | 58 ---------------------------- maths/find_min.py | 51 ++++++++++++++++++++++++ maths/find_min_recursion.py | 58 ---------------------------- maths/gamma.py | 76 +++++++++++++++++++++++++++++++++++- maths/gamma_recursive.py | 77 ------------------------------------- 6 files changed, 176 insertions(+), 195 deletions(-) delete mode 100644 maths/find_max_recursion.py delete mode 100644 maths/find_min_recursion.py delete mode 100644 maths/gamma_recursive.py diff --git a/maths/find_max.py b/maths/find_max.py index 684fbe8161e8..d690b4ddb305 100644 --- a/maths/find_max.py +++ b/maths/find_max.py @@ -25,6 +25,57 @@ def find_max(nums: list[int | float]) -> int | float: return max_num +# Divide and Conquer algorithm +def find_max_recursive(nums: list[int | float], left: int, right: int) -> int | float: + """ + find max value in list + :param nums: contains elements + :param left: index of first element + :param right: index of last element + :return: max in nums + + >>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]): + ... find_max(nums, 0, len(nums) - 1) == max(nums) + True + True + True + True + >>> nums = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] + >>> find_max(nums, 0, len(nums) - 1) == max(nums) + True + >>> find_max([], 0, 0) + Traceback (most recent call last): + ... + ValueError: find_max() arg is an empty sequence + >>> find_max(nums, 0, len(nums)) == max(nums) + Traceback (most recent call last): + ... + IndexError: list index out of range + >>> find_max(nums, -len(nums), -1) == max(nums) + True + >>> find_max(nums, -len(nums) - 1, -1) == max(nums) + Traceback (most recent call last): + ... + IndexError: list index out of range + """ + if len(nums) == 0: + raise ValueError("find_max() arg is an empty sequence") + if ( + left >= len(nums) + or left < -len(nums) + or right >= len(nums) + or right < -len(nums) + ): + raise IndexError("list index out of range") + if left == right: + return nums[left] + mid = (left + right) >> 1 # the middle + left_max = find_max(nums, left, mid) # find max in range[left, mid] + right_max = find_max(nums, mid + 1, right) # find max in range[mid + 1, right] + + return left_max if left_max >= right_max else right_max + + if __name__ == "__main__": import doctest diff --git a/maths/find_max_recursion.py b/maths/find_max_recursion.py deleted file mode 100644 index 629932e0818f..000000000000 --- a/maths/find_max_recursion.py +++ /dev/null @@ -1,58 +0,0 @@ -from __future__ import annotations - - -# Divide and Conquer algorithm -def find_max(nums: list[int | float], left: int, right: int) -> int | float: - """ - find max value in list - :param nums: contains elements - :param left: index of first element - :param right: index of last element - :return: max in nums - - >>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]): - ... find_max(nums, 0, len(nums) - 1) == max(nums) - True - True - True - True - >>> nums = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] - >>> find_max(nums, 0, len(nums) - 1) == max(nums) - True - >>> find_max([], 0, 0) - Traceback (most recent call last): - ... - ValueError: find_max() arg is an empty sequence - >>> find_max(nums, 0, len(nums)) == max(nums) - Traceback (most recent call last): - ... - IndexError: list index out of range - >>> find_max(nums, -len(nums), -1) == max(nums) - True - >>> find_max(nums, -len(nums) - 1, -1) == max(nums) - Traceback (most recent call last): - ... - IndexError: list index out of range - """ - if len(nums) == 0: - raise ValueError("find_max() arg is an empty sequence") - if ( - left >= len(nums) - or left < -len(nums) - or right >= len(nums) - or right < -len(nums) - ): - raise IndexError("list index out of range") - if left == right: - return nums[left] - mid = (left + right) >> 1 # the middle - left_max = find_max(nums, left, mid) # find max in range[left, mid] - right_max = find_max(nums, mid + 1, right) # find max in range[mid + 1, right] - - return left_max if left_max >= right_max else right_max - - -if __name__ == "__main__": - import doctest - - doctest.testmod(verbose=True) diff --git a/maths/find_min.py b/maths/find_min.py index 2eac087c6388..ba0f7b1dcf9d 100644 --- a/maths/find_min.py +++ b/maths/find_min.py @@ -28,6 +28,57 @@ def find_min(nums: list[int | float]) -> int | float: return min_num +# Divide and Conquer algorithm +def find_min(nums: list[int | float], left: int, right: int) -> int | float: + """ + find min value in list + :param nums: contains elements + :param left: index of first element + :param right: index of last element + :return: min in nums + + >>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]): + ... find_min(nums, 0, len(nums) - 1) == min(nums) + True + True + True + True + >>> nums = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] + >>> find_min(nums, 0, len(nums) - 1) == min(nums) + True + >>> find_min([], 0, 0) + Traceback (most recent call last): + ... + ValueError: find_min() arg is an empty sequence + >>> find_min(nums, 0, len(nums)) == min(nums) + Traceback (most recent call last): + ... + IndexError: list index out of range + >>> find_min(nums, -len(nums), -1) == min(nums) + True + >>> find_min(nums, -len(nums) - 1, -1) == min(nums) + Traceback (most recent call last): + ... + IndexError: list index out of range + """ + if len(nums) == 0: + raise ValueError("find_min() arg is an empty sequence") + if ( + left >= len(nums) + or left < -len(nums) + or right >= len(nums) + or right < -len(nums) + ): + raise IndexError("list index out of range") + if left == right: + return nums[left] + mid = (left + right) >> 1 # the middle + left_min = find_min(nums, left, mid) # find min in range[left, mid] + right_min = find_min(nums, mid + 1, right) # find min in range[mid + 1, right] + + return left_min if left_min <= right_min else right_min + + if __name__ == "__main__": import doctest diff --git a/maths/find_min_recursion.py b/maths/find_min_recursion.py deleted file mode 100644 index 4d11015efcd5..000000000000 --- a/maths/find_min_recursion.py +++ /dev/null @@ -1,58 +0,0 @@ -from __future__ import annotations - - -# Divide and Conquer algorithm -def find_min(nums: list[int | float], left: int, right: int) -> int | float: - """ - find min value in list - :param nums: contains elements - :param left: index of first element - :param right: index of last element - :return: min in nums - - >>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]): - ... find_min(nums, 0, len(nums) - 1) == min(nums) - True - True - True - True - >>> nums = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] - >>> find_min(nums, 0, len(nums) - 1) == min(nums) - True - >>> find_min([], 0, 0) - Traceback (most recent call last): - ... - ValueError: find_min() arg is an empty sequence - >>> find_min(nums, 0, len(nums)) == min(nums) - Traceback (most recent call last): - ... - IndexError: list index out of range - >>> find_min(nums, -len(nums), -1) == min(nums) - True - >>> find_min(nums, -len(nums) - 1, -1) == min(nums) - Traceback (most recent call last): - ... - IndexError: list index out of range - """ - if len(nums) == 0: - raise ValueError("find_min() arg is an empty sequence") - if ( - left >= len(nums) - or left < -len(nums) - or right >= len(nums) - or right < -len(nums) - ): - raise IndexError("list index out of range") - if left == right: - return nums[left] - mid = (left + right) >> 1 # the middle - left_min = find_min(nums, left, mid) # find min in range[left, mid] - right_min = find_min(nums, mid + 1, right) # find min in range[mid + 1, right] - - return left_min if left_min <= right_min else right_min - - -if __name__ == "__main__": - import doctest - - doctest.testmod(verbose=True) diff --git a/maths/gamma.py b/maths/gamma.py index d5debc58764b..5880aa3b0582 100644 --- a/maths/gamma.py +++ b/maths/gamma.py @@ -1,4 +1,11 @@ -import math +""" +Gamma function is a very useful tool in math and physics. +It helps calculating complex integral in a convenient way. +for more info: https://en.wikipedia.org/wiki/Gamma_function +Python's Standard Library gamma() function overflows around gamma(171.624). +""" + +from math import pi, sqrt, exp from numpy import inf from scipy.integrate import quad @@ -39,10 +46,75 @@ def gamma(num: float) -> float: def integrand(x: float, z: float) -> float: - return math.pow(x, z - 1) * math.exp(-x) + return pow(x, z - 1) * exp(-x) + + +def gamma_recursive(num: float) -> float: + """ + Calculates the value of Gamma function of num + where num is either an integer (1, 2, 3..) or a half-integer (0.5, 1.5, 2.5 ...). + Implemented using recursion + Examples: + >>> from math import isclose, gamma as math_gamma + >>> gamma(0.5) + 1.7724538509055159 + >>> gamma(2) + 1.0 + >>> gamma(3.5) + 3.3233509704478426 + >>> gamma(171.5) + 9.483367566824795e+307 + >>> all(isclose(gamma(num), math_gamma(num)) for num in (0.5, 2, 3.5, 171.5)) + True + >>> gamma(0) + Traceback (most recent call last): + ... + ValueError: math domain error + >>> gamma(-1.1) + Traceback (most recent call last): + ... + ValueError: math domain error + >>> gamma(-4) + Traceback (most recent call last): + ... + ValueError: math domain error + >>> gamma(172) + Traceback (most recent call last): + ... + OverflowError: math range error + >>> gamma(1.1) + Traceback (most recent call last): + ... + NotImplementedError: num must be an integer or a half-integer + """ + if num <= 0: + raise ValueError("math domain error") + if num > 171.5: + raise OverflowError("math range error") + elif num - int(num) not in (0, 0.5): + raise NotImplementedError("num must be an integer or a half-integer") + elif num == 0.5: + return sqrt(pi) + else: + return 1.0 if num == 1 else (num - 1) * gamma(num - 1) + +def test_gamma() -> None: + """ + >>> test_gamma() + """ + assert gamma(0.5) == sqrt(pi) + assert gamma(1) == 1.0 + assert gamma(2) == 1.0 + if __name__ == "__main__": from doctest import testmod testmod() + + num = 1.0 + while num: + num = float(input("Gamma of: ")) + print(f"gamma({num}) = {gamma(num)}") + print("\nEnter 0 to exit...") \ No newline at end of file diff --git a/maths/gamma_recursive.py b/maths/gamma_recursive.py deleted file mode 100644 index 3d6b8c5e8138..000000000000 --- a/maths/gamma_recursive.py +++ /dev/null @@ -1,77 +0,0 @@ -""" -Gamma function is a very useful tool in math and physics. -It helps calculating complex integral in a convenient way. -for more info: https://en.wikipedia.org/wiki/Gamma_function -Python's Standard Library math.gamma() function overflows around gamma(171.624). -""" -from math import pi, sqrt - - -def gamma(num: float) -> float: - """ - Calculates the value of Gamma function of num - where num is either an integer (1, 2, 3..) or a half-integer (0.5, 1.5, 2.5 ...). - Implemented using recursion - Examples: - >>> from math import isclose, gamma as math_gamma - >>> gamma(0.5) - 1.7724538509055159 - >>> gamma(2) - 1.0 - >>> gamma(3.5) - 3.3233509704478426 - >>> gamma(171.5) - 9.483367566824795e+307 - >>> all(isclose(gamma(num), math_gamma(num)) for num in (0.5, 2, 3.5, 171.5)) - True - >>> gamma(0) - Traceback (most recent call last): - ... - ValueError: math domain error - >>> gamma(-1.1) - Traceback (most recent call last): - ... - ValueError: math domain error - >>> gamma(-4) - Traceback (most recent call last): - ... - ValueError: math domain error - >>> gamma(172) - Traceback (most recent call last): - ... - OverflowError: math range error - >>> gamma(1.1) - Traceback (most recent call last): - ... - NotImplementedError: num must be an integer or a half-integer - """ - if num <= 0: - raise ValueError("math domain error") - if num > 171.5: - raise OverflowError("math range error") - elif num - int(num) not in (0, 0.5): - raise NotImplementedError("num must be an integer or a half-integer") - elif num == 0.5: - return sqrt(pi) - else: - return 1.0 if num == 1 else (num - 1) * gamma(num - 1) - - -def test_gamma() -> None: - """ - >>> test_gamma() - """ - assert gamma(0.5) == sqrt(pi) - assert gamma(1) == 1.0 - assert gamma(2) == 1.0 - - -if __name__ == "__main__": - from doctest import testmod - - testmod() - num = 1.0 - while num: - num = float(input("Gamma of: ")) - print(f"gamma({num}) = {gamma(num)}") - print("\nEnter 0 to exit...") From 1ef07fc72e6b55c60a5641efb6cdbc34b30a46b2 Mon Sep 17 00:00:00 2001 From: CaedenPH Date: Wed, 25 Jan 2023 19:45:22 +0000 Subject: [PATCH 03/10] feat: Concatenate algorithms --- maths/double_factorial_iterative.py | 33 ----------------------------- maths/double_factorial_recursive.py | 33 +++++++++++++++++++++++++++-- maths/factorial.py | 2 +- maths/find_max.py | 8 ++++--- maths/find_min.py | 10 +++++---- maths/gamma.py | 6 +++--- 6 files changed, 46 insertions(+), 46 deletions(-) delete mode 100644 maths/double_factorial_iterative.py diff --git a/maths/double_factorial_iterative.py b/maths/double_factorial_iterative.py deleted file mode 100644 index b2b58aa04c28..000000000000 --- a/maths/double_factorial_iterative.py +++ /dev/null @@ -1,33 +0,0 @@ -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 - - -if __name__ == "__main__": - import doctest - - doctest.testmod() diff --git a/maths/double_factorial_recursive.py b/maths/double_factorial_recursive.py index 05c9b29680a7..2997c78fb9c7 100644 --- a/maths/double_factorial_recursive.py +++ b/maths/double_factorial_recursive.py @@ -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. @@ -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__": diff --git a/maths/factorial.py b/maths/factorial.py index 3aa5821813ae..bbf0efc011d8 100644 --- a/maths/factorial.py +++ b/maths/factorial.py @@ -34,7 +34,7 @@ def factorial(number: int) -> int: return value -def factorial(n: int) -> int: +def factorial_recursive(n: int) -> int: """ Calculate the factorial of a positive integer https://en.wikipedia.org/wiki/Factorial diff --git a/maths/find_max.py b/maths/find_max.py index d690b4ddb305..bc8870e2aba5 100644 --- a/maths/find_max.py +++ b/maths/find_max.py @@ -59,7 +59,7 @@ def find_max_recursive(nums: list[int | float], left: int, right: int) -> int | IndexError: list index out of range """ if len(nums) == 0: - raise ValueError("find_max() arg is an empty sequence") + raise ValueError("find_max_recursive() arg is an empty sequence") if ( left >= len(nums) or left < -len(nums) @@ -70,8 +70,10 @@ def find_max_recursive(nums: list[int | float], left: int, right: int) -> int | if left == right: return nums[left] mid = (left + right) >> 1 # the middle - left_max = find_max(nums, left, mid) # find max in range[left, mid] - right_max = find_max(nums, mid + 1, right) # find max in range[mid + 1, right] + left_max = find_max_recursive(nums, left, mid) # find max in range[left, mid] + right_max = find_max_recursive( + nums, mid + 1, right + ) # find max in range[mid + 1, right] return left_max if left_max >= right_max else right_max diff --git a/maths/find_min.py b/maths/find_min.py index ba0f7b1dcf9d..2016c412bb2e 100644 --- a/maths/find_min.py +++ b/maths/find_min.py @@ -29,7 +29,7 @@ def find_min(nums: list[int | float]) -> int | float: # Divide and Conquer algorithm -def find_min(nums: list[int | float], left: int, right: int) -> int | float: +def find_min_recursive(nums: list[int | float], left: int, right: int) -> int | float: """ find min value in list :param nums: contains elements @@ -62,7 +62,7 @@ def find_min(nums: list[int | float], left: int, right: int) -> int | float: IndexError: list index out of range """ if len(nums) == 0: - raise ValueError("find_min() arg is an empty sequence") + raise ValueError("find_min_recursive() arg is an empty sequence") if ( left >= len(nums) or left < -len(nums) @@ -73,8 +73,10 @@ def find_min(nums: list[int | float], left: int, right: int) -> int | float: if left == right: return nums[left] mid = (left + right) >> 1 # the middle - left_min = find_min(nums, left, mid) # find min in range[left, mid] - right_min = find_min(nums, mid + 1, right) # find min in range[mid + 1, right] + left_min = find_min_recursive(nums, left, mid) # find min in range[left, mid] + right_min = find_min_recursive( + nums, mid + 1, right + ) # find min in range[mid + 1, right] return left_min if left_min <= right_min else right_min diff --git a/maths/gamma.py b/maths/gamma.py index 5880aa3b0582..e0abeb01f345 100644 --- a/maths/gamma.py +++ b/maths/gamma.py @@ -5,7 +5,7 @@ Python's Standard Library gamma() function overflows around gamma(171.624). """ -from math import pi, sqrt, exp +from math import exp, pi, sqrt from numpy import inf from scipy.integrate import quad @@ -106,7 +106,7 @@ def test_gamma() -> None: assert gamma(0.5) == sqrt(pi) assert gamma(1) == 1.0 assert gamma(2) == 1.0 - + if __name__ == "__main__": from doctest import testmod @@ -117,4 +117,4 @@ def test_gamma() -> None: while num: num = float(input("Gamma of: ")) print(f"gamma({num}) = {gamma(num)}") - print("\nEnter 0 to exit...") \ No newline at end of file + print("\nEnter 0 to exit...") From 453c6372c7b36bfb2c5a9f7588bec1c0e809002c Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Thu, 26 Jan 2023 12:24:23 +0000 Subject: [PATCH 04/10] refactor: Concatenate algorithms --- maths/binary_exp_mod.py | 24 ++++++++++++ maths/binary_exponentiation.py | 67 +++++++++++++++++++++++++++----- maths/binary_exponentiation_2.py | 29 -------------- maths/binary_exponentiation_3.py | 58 ++++----------------------- 4 files changed, 90 insertions(+), 88 deletions(-) diff --git a/maths/binary_exp_mod.py b/maths/binary_exp_mod.py index 67dd1e728b18..72555d210c03 100644 --- a/maths/binary_exp_mod.py +++ b/maths/binary_exp_mod.py @@ -17,6 +17,30 @@ def bin_exp_mod(a, n, b): return (r * r) % b +def binary_exponentiation_mod_addition(a, b, c): + 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): + 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()) diff --git a/maths/binary_exponentiation.py b/maths/binary_exponentiation.py index 8dda5245cf44..8cb3d3bfb691 100644 --- a/maths/binary_exponentiation.py +++ b/maths/binary_exponentiation.py @@ -1,20 +1,69 @@ -"""Binary Exponentiation.""" -# Author : Junth Basnet -# Time Complexity : O(logn) -def binary_exponentiation(a, n): +def binary_exponentiation_multiplication(a, b): + """ + * 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 - if n == 0: + * @author chinmoy159 + * @version 1.0 dated 10/08/2017 + """ + + res = 0 + while b > 0: + if b & 1: + res += a + + 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 + + * @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 + 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__": @@ -24,5 +73,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}") diff --git a/maths/binary_exponentiation_2.py b/maths/binary_exponentiation_2.py index 51ec4baf2598..e79f08b698df 100644 --- a/maths/binary_exponentiation_2.py +++ b/maths/binary_exponentiation_2.py @@ -1,38 +1,9 @@ -""" -* 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 chinmoy159 -* @version 1.0 dated 10/08/2017 -""" - - -def b_expo(a, b): - res = 0 - while b > 0: - if b & 1: - res += a - a += a - b >>= 1 - return res -def b_expo_mod(a, b, c): - res = 0 - while b > 0: - if b & 1: - res = ((res % c) + (a % c)) % c - a += a - b >>= 1 - return res """ diff --git a/maths/binary_exponentiation_3.py b/maths/binary_exponentiation_3.py index dd4e70e74129..12a6f48e59e8 100644 --- a/maths/binary_exponentiation_3.py +++ b/maths/binary_exponentiation_3.py @@ -1,50 +1,8 @@ -""" -* 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 - -* @author chinmoy159 -* @version 1.0 dated 10/08/2017 -""" - - -def b_expo(a, b): - res = 1 - while b > 0: - if b & 1: - res *= a - - a *= a - b >>= 1 - - return res - - -def b_expo_mod(a, b, c): - res = 1 - while b > 0: - if b & 1: - res = ((res % c) * (a % c)) % c - - a *= a - b >>= 1 - - return res - - -""" -* Wondering how this method works ! -* It's pretty simple. -* 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. -""" + + + + + + + + From 10b43d53877a4c4d4632f4f777ad4afe9ea359e3 Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Thu, 26 Jan 2023 22:17:51 +0000 Subject: [PATCH 05/10] refactor: Concatenate duplicate algorithms --- .../addition_without_arithmetic.py | 0 maths/binary_exp_mod.py | 48 ++++++++++++++++++- maths/binary_exponentiation.py | 19 ++++++-- maths/binary_exponentiation_2.py | 21 -------- maths/binary_exponentiation_3.py | 8 ---- 5 files changed, 60 insertions(+), 36 deletions(-) rename {maths => bit_manipulation}/addition_without_arithmetic.py (100%) delete mode 100644 maths/binary_exponentiation_2.py delete mode 100644 maths/binary_exponentiation_3.py diff --git a/maths/addition_without_arithmetic.py b/bit_manipulation/addition_without_arithmetic.py similarity index 100% rename from maths/addition_without_arithmetic.py rename to bit_manipulation/addition_without_arithmetic.py diff --git a/maths/binary_exp_mod.py b/maths/binary_exp_mod.py index 72555d210c03..ec205383f09c 100644 --- a/maths/binary_exp_mod.py +++ b/maths/binary_exp_mod.py @@ -17,7 +17,29 @@ def bin_exp_mod(a, n, b): return (r * r) % b -def binary_exponentiation_mod_addition(a, b, c): +def binary_exponentiation_mod_multiplication(a, b, c): + """ + * 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: @@ -29,7 +51,29 @@ def binary_exponentiation_mod_addition(a, b, c): return res -def binary_exponentiation__mod_powers(a, b, c): +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: diff --git a/maths/binary_exponentiation.py b/maths/binary_exponentiation.py index 8cb3d3bfb691..b58e0781167d 100644 --- a/maths/binary_exponentiation.py +++ b/maths/binary_exponentiation.py @@ -1,6 +1,3 @@ - - - def binary_exponentiation_multiplication(a, b): """ * Binary Exponentiation with Multiplication @@ -10,6 +7,12 @@ def binary_exponentiation_multiplication(a, b): * 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 + * @author chinmoy159 * @version 1.0 dated 10/08/2017 """ @@ -34,6 +37,12 @@ def binary_exponentiation_powers(a, b): * 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 """ @@ -51,7 +60,7 @@ def binary_exponentiation_powers(a, b): def binary_exponentiation_recursion(a, b): """Binary Exponentiation with recursion. - + * Time Complexity : O(logn) * @author : Junth Basnet """ @@ -59,7 +68,7 @@ def binary_exponentiation_recursion(a, b): if b == 0: return 1 - elif n % 2 == 1: + elif b % 2 == 1: return binary_exponentiation_recursion(a, b - 1) * a else: diff --git a/maths/binary_exponentiation_2.py b/maths/binary_exponentiation_2.py deleted file mode 100644 index e79f08b698df..000000000000 --- a/maths/binary_exponentiation_2.py +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - -""" -* Wondering how this method works ! -* It's pretty simple. -* 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. -""" diff --git a/maths/binary_exponentiation_3.py b/maths/binary_exponentiation_3.py deleted file mode 100644 index 12a6f48e59e8..000000000000 --- a/maths/binary_exponentiation_3.py +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - From 95c5ec093212f1fd6b249fd704fd34ed624521b7 Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Fri, 27 Jan 2023 07:45:37 +0000 Subject: [PATCH 06/10] fix: Algorithm concatenation --- maths/find_max.py | 14 +++++++------- maths/find_min.py | 14 +++++++------- maths/gamma.py | 22 +++++++++++----------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/maths/find_max.py b/maths/find_max.py index bc8870e2aba5..b50f2dfe5279 100644 --- a/maths/find_max.py +++ b/maths/find_max.py @@ -35,25 +35,25 @@ def find_max_recursive(nums: list[int | float], left: int, right: int) -> int | :return: max in nums >>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]): - ... find_max(nums, 0, len(nums) - 1) == max(nums) + ... find_max_recursive(nums, 0, len(nums) - 1) == max(nums) True True True True >>> nums = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] - >>> find_max(nums, 0, len(nums) - 1) == max(nums) + >>> find_max_recursive(nums, 0, len(nums) - 1) == max(nums) True - >>> find_max([], 0, 0) + >>> find_max_recursive([], 0, 0) Traceback (most recent call last): ... - ValueError: find_max() arg is an empty sequence - >>> find_max(nums, 0, len(nums)) == max(nums) + ValueError: find_max_recursive() arg is an empty sequence + >>> find_max_recursive(nums, 0, len(nums)) == max(nums) Traceback (most recent call last): ... IndexError: list index out of range - >>> find_max(nums, -len(nums), -1) == max(nums) + >>> find_max_recursive(nums, -len(nums), -1) == max(nums) True - >>> find_max(nums, -len(nums) - 1, -1) == max(nums) + >>> find_max_recursive(nums, -len(nums) - 1, -1) == max(nums) Traceback (most recent call last): ... IndexError: list index out of range diff --git a/maths/find_min.py b/maths/find_min.py index 2016c412bb2e..7e347dafe55d 100644 --- a/maths/find_min.py +++ b/maths/find_min.py @@ -38,25 +38,25 @@ def find_min_recursive(nums: list[int | float], left: int, right: int) -> int | :return: min in nums >>> for nums in ([3, 2, 1], [-3, -2, -1], [3, -3, 0], [3.0, 3.1, 2.9]): - ... find_min(nums, 0, len(nums) - 1) == min(nums) + ... find_min_recursive(nums, 0, len(nums) - 1) == min(nums) True True True True >>> nums = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] - >>> find_min(nums, 0, len(nums) - 1) == min(nums) + >>> find_min_recursive(nums, 0, len(nums) - 1) == min(nums) True - >>> find_min([], 0, 0) + >>> find_min_recursive([], 0, 0) Traceback (most recent call last): ... - ValueError: find_min() arg is an empty sequence - >>> find_min(nums, 0, len(nums)) == min(nums) + ValueError: find_min_recursive() arg is an empty sequence + >>> find_min_recursive(nums, 0, len(nums)) == min(nums) Traceback (most recent call last): ... IndexError: list index out of range - >>> find_min(nums, -len(nums), -1) == min(nums) + >>> find_min_recursive(nums, -len(nums), -1) == min(nums) True - >>> find_min(nums, -len(nums) - 1, -1) == min(nums) + >>> find_min_recursive(nums, -len(nums) - 1, -1) == min(nums) Traceback (most recent call last): ... IndexError: list index out of range diff --git a/maths/gamma.py b/maths/gamma.py index e0abeb01f345..04ed8fe32d07 100644 --- a/maths/gamma.py +++ b/maths/gamma.py @@ -56,33 +56,33 @@ def gamma_recursive(num: float) -> float: Implemented using recursion Examples: >>> from math import isclose, gamma as math_gamma - >>> gamma(0.5) + >>> gamma_recursive(0.5) 1.7724538509055159 - >>> gamma(2) + >>> gamma_recursive(2) 1.0 - >>> gamma(3.5) + >>> gamma_recursive(3.5) 3.3233509704478426 - >>> gamma(171.5) + >>> gamma_recursive(171.5) 9.483367566824795e+307 - >>> all(isclose(gamma(num), math_gamma(num)) for num in (0.5, 2, 3.5, 171.5)) + >>> all(isclose(gamma_recursive(num), math_gamma(num)) for num in (0.5, 2, 3.5, 171.5)) True - >>> gamma(0) + >>> gamma_recursive(0) Traceback (most recent call last): ... ValueError: math domain error - >>> gamma(-1.1) + >>> gamma_recursive(-1.1) Traceback (most recent call last): ... ValueError: math domain error - >>> gamma(-4) + >>> gamma_recursive(-4) Traceback (most recent call last): ... ValueError: math domain error - >>> gamma(172) + >>> gamma_recursive(172) Traceback (most recent call last): ... OverflowError: math range error - >>> gamma(1.1) + >>> gamma_recursive(1.1) Traceback (most recent call last): ... NotImplementedError: num must be an integer or a half-integer @@ -96,7 +96,7 @@ def gamma_recursive(num: float) -> float: elif num == 0.5: return sqrt(pi) else: - return 1.0 if num == 1 else (num - 1) * gamma(num - 1) + return 1.0 if num == 1 else (num - 1) * gamma_recursive(num - 1) def test_gamma() -> None: From a8a6be5b0c65efbf95c7752301de83cd7f1e1669 Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Fri, 27 Jan 2023 07:49:08 +0000 Subject: [PATCH 07/10] fix: Correct line length --- maths/gamma.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/maths/gamma.py b/maths/gamma.py index 04ed8fe32d07..79c9cf199bc7 100644 --- a/maths/gamma.py +++ b/maths/gamma.py @@ -64,7 +64,8 @@ def gamma_recursive(num: float) -> float: 3.3233509704478426 >>> gamma_recursive(171.5) 9.483367566824795e+307 - >>> all(isclose(gamma_recursive(num), math_gamma(num)) for num in (0.5, 2, 3.5, 171.5)) + >>> all(\ + isclose(gamma_recursive(num), math_gamma(num)) for num in (0.5, 2, 3.5, 171.5)) True >>> gamma_recursive(0) Traceback (most recent call last): @@ -103,9 +104,9 @@ def test_gamma() -> None: """ >>> test_gamma() """ - assert gamma(0.5) == sqrt(pi) - assert gamma(1) == 1.0 - assert gamma(2) == 1.0 + assert gamma_recursive(0.5) == sqrt(pi) + assert gamma_recursive(1) == 1.0 + assert gamma_recursive(2) == 1.0 if __name__ == "__main__": From 448cd6798dcb1df7455265cc4dadbab0f7c41686 Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Mon, 6 Feb 2023 21:55:45 +0000 Subject: [PATCH 08/10] Update maths/find_min.py Co-authored-by: Tianyi Zheng --- maths/find_min.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maths/find_min.py b/maths/find_min.py index 7e347dafe55d..1a0959924072 100644 --- a/maths/find_min.py +++ b/maths/find_min.py @@ -72,7 +72,7 @@ def find_min_recursive(nums: list[int | float], left: int, right: int) -> int | raise IndexError("list index out of range") if left == right: return nums[left] - mid = (left + right) >> 1 # the middle + mid = (left + right) // 2 # the middle left_min = find_min_recursive(nums, left, mid) # find min in range[left, mid] right_min = find_min_recursive( nums, mid + 1, right From 91f4af788baeadb0598843fa8b0de197bbd8e6c5 Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Mon, 6 Feb 2023 21:55:54 +0000 Subject: [PATCH 09/10] Update maths/find_max.py Co-authored-by: Tianyi Zheng --- maths/find_max.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maths/find_max.py b/maths/find_max.py index b50f2dfe5279..8af8f5e0e334 100644 --- a/maths/find_max.py +++ b/maths/find_max.py @@ -69,7 +69,7 @@ def find_max_recursive(nums: list[int | float], left: int, right: int) -> int | raise IndexError("list index out of range") if left == right: return nums[left] - mid = (left + right) >> 1 # the middle + mid = (left + right) // 2 # the middle left_max = find_max_recursive(nums, left, mid) # find max in range[left, mid] right_max = find_max_recursive( nums, mid + 1, right From be3b3e82b66e11dd44aae5ca25f94bf7e289086e Mon Sep 17 00:00:00 2001 From: Caeden Perelli-Harris Date: Mon, 6 Feb 2023 21:56:00 +0000 Subject: [PATCH 10/10] Update maths/binary_exponentiation.py Co-authored-by: Tianyi Zheng --- maths/binary_exponentiation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maths/binary_exponentiation.py b/maths/binary_exponentiation.py index b58e0781167d..7e528a9f52f7 100644 --- a/maths/binary_exponentiation.py +++ b/maths/binary_exponentiation.py @@ -7,7 +7,7 @@ def binary_exponentiation_multiplication(a, b): * 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 + * 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