From f5211cc56d11c815a7183c880b548b8d1c5564e2 Mon Sep 17 00:00:00 2001 From: Acuspeedster Date: Tue, 1 Oct 2024 21:43:13 +0530 Subject: [PATCH 01/22] feat: add Matrix Exponentiation method docs: updated the header documentation and added new documentation for the new function. --- maths/fibonacci.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/maths/fibonacci.py b/maths/fibonacci.py index 927700b0418e..cfa813188752 100644 --- a/maths/fibonacci.py +++ b/maths/fibonacci.py @@ -7,6 +7,8 @@ NOTE 2: the Binet's formula function is much more limited in the size of inputs that it can handle due to the size limitations of Python floats +NOTE 3: the matrix function is the fastest and most memory efficient for large n + See benchmark numbers in __main__ for performance comparisons/ https://en.wikipedia.org/wiki/Fibonacci_number for more information @@ -230,6 +232,32 @@ def fib_binet(n: int) -> list[int]: return [round(phi**i / sqrt_5) for i in range(n + 1)] +def fib_matrix(n: int) -> int: + def matrix_mult(a, b): + return [[a[0][0] * b[0][0] + a[0][1] * b[1][0], + a[0][0] * b[0][1] + a[0][1] * b[1][1]], + [a[1][0] * b[0][0] + a[1][1] * b[1][0], + a[1][0] * b[0][1] + a[1][1] * b[1][1]]] + + def matrix_pow(m, power): + result = [[1, 0], [0, 1]] # Identity matrix + base = m + while power: + if power % 2 == 1: + result = matrix_mult(result, base) + base = matrix_mult(base, base) + power //= 2 + return result + + if n < 0: + raise ValueError("n is negative") + if n == 0: + return 0 + m = [[1, 1], [1, 0]] + result = matrix_pow(m, n - 1) + return result[0][0] + + if __name__ == "__main__": from doctest import testmod @@ -242,3 +270,4 @@ def fib_binet(n: int) -> list[int]: time_func(fib_memoization, num) # 0.0100 ms time_func(fib_recursive_cached, num) # 0.0153 ms time_func(fib_recursive, num) # 257.0910 ms + time_func(fib_matrix, num) # 0.0000 ms From 95421d7b9a8620c530d211e2cf8e2aeef4ff2f2e Mon Sep 17 00:00:00 2001 From: Acuspeedster Date: Tue, 1 Oct 2024 21:52:31 +0530 Subject: [PATCH 02/22] feat: added new function matrix exponetiation method --- maths/fibonacci.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/maths/fibonacci.py b/maths/fibonacci.py index cfa813188752..fb40e24d8dd5 100644 --- a/maths/fibonacci.py +++ b/maths/fibonacci.py @@ -233,6 +233,8 @@ def fib_binet(n: int) -> list[int]: def fib_matrix(n: int) -> int: + # https://www.nayuki.io/page/fast-fibonacci-algorithms#:~:text= + # Summary:%20The%20two%20fast%20Fibonacci%20algorithms%20are%20matrix def matrix_mult(a, b): return [[a[0][0] * b[0][0] + a[0][1] * b[1][0], a[0][0] * b[0][1] + a[0][1] * b[1][1]], From adf10bf3b7ea8b1e54b895497c2963cb8236a007 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 16:26:52 +0000 Subject: [PATCH 03/22] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- maths/fibonacci.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/maths/fibonacci.py b/maths/fibonacci.py index fb40e24d8dd5..02288204da18 100644 --- a/maths/fibonacci.py +++ b/maths/fibonacci.py @@ -236,10 +236,16 @@ def fib_matrix(n: int) -> int: # https://www.nayuki.io/page/fast-fibonacci-algorithms#:~:text= # Summary:%20The%20two%20fast%20Fibonacci%20algorithms%20are%20matrix def matrix_mult(a, b): - return [[a[0][0] * b[0][0] + a[0][1] * b[1][0], - a[0][0] * b[0][1] + a[0][1] * b[1][1]], - [a[1][0] * b[0][0] + a[1][1] * b[1][0], - a[1][0] * b[0][1] + a[1][1] * b[1][1]]] + return [ + [ + a[0][0] * b[0][0] + a[0][1] * b[1][0], + a[0][0] * b[0][1] + a[0][1] * b[1][1], + ], + [ + a[1][0] * b[0][0] + a[1][1] * b[1][0], + a[1][0] * b[0][1] + a[1][1] * b[1][1], + ], + ] def matrix_pow(m, power): result = [[1, 0], [0, 1]] # Identity matrix From ba1cbc6e8bff7195c5ed16599f479fbff1514f53 Mon Sep 17 00:00:00 2001 From: Acuspeedster Date: Tue, 1 Oct 2024 22:34:25 +0530 Subject: [PATCH 04/22] feat: This function uses the tail-recursive form of the Euclidean algorithm to calculate --- maths/gcd_of_n_numbers.py | 46 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/maths/gcd_of_n_numbers.py b/maths/gcd_of_n_numbers.py index 63236c236ada..9c61c91a6b56 100644 --- a/maths/gcd_of_n_numbers.py +++ b/maths/gcd_of_n_numbers.py @@ -1,6 +1,7 @@ """ Gcd of N Numbers Reference: https://en.wikipedia.org/wiki/Greatest_common_divisor +Reference for tail recursion: https://www.geeksforgeeks.org/tail-recursion/ """ from collections import Counter @@ -104,6 +105,51 @@ def get_greatest_common_divisor(*numbers: int) -> int: mult *= m return mult +def gcd_tail_recursive(a: int, b: int) -> int: + """ + Calculate the Greatest Common Divisor (GCD) using a tail-recursive approach. + + This function uses the tail-recursive form of the Euclidean algorithm to calculate + the GCD of two integers `a` and `b`. The GCD is the largest integer that divides both + `a` and `b` without leaving a remainder. + + Tail recursion is a form of recursion where the recursive call is the last operation + in the function. In languages that support tail call optimization, this allows the + function to be optimized by reusing the current function's stack frame for the + next call. Python, however, does not support tail call optimization, but using this + style can still help structure the recursion for better clarity. + + Args: + a (int): The first integer. + b (int): The second integer. + + Returns: + int: The greatest common divisor of `a` and `b`. + + Raises: + ValueError: If both `a` and `b` are zero, as the GCD is not defined for this case. + + Example: + >>> gcd_tail_recursive(24, 40) + 8 + >>> gcd_tail_recursive(11, 37) + 1 + >>> gcd_tail_recursive(0, 5) + 5 + >>> gcd_tail_recursive(5, 0) + 5 + >>> gcd_tail_recursive(0, 0) + ValueError: GCD is not defined for both a and b being zero. + + Notes: + - gcd(a, 0) = abs(a) + - gcd(0, b) = abs(b) + - gcd(0, 0) is undefined. + """ + if b == 0: + return abs(a) + return gcd_tail_recursive(b, a % b) if __name__ == "__main__": print(get_greatest_common_divisor(18, 45)) # 9 + print(gcd_tail_recursive(23, 37)) # 1 From fd37108779d40ecfb161900b6bc296c479f67c28 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 17:05:15 +0000 Subject: [PATCH 05/22] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- maths/gcd_of_n_numbers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/maths/gcd_of_n_numbers.py b/maths/gcd_of_n_numbers.py index 9c61c91a6b56..da6b767a1b22 100644 --- a/maths/gcd_of_n_numbers.py +++ b/maths/gcd_of_n_numbers.py @@ -105,6 +105,7 @@ def get_greatest_common_divisor(*numbers: int) -> int: mult *= m return mult + def gcd_tail_recursive(a: int, b: int) -> int: """ Calculate the Greatest Common Divisor (GCD) using a tail-recursive approach. @@ -150,6 +151,7 @@ def gcd_tail_recursive(a: int, b: int) -> int: return abs(a) return gcd_tail_recursive(b, a % b) + if __name__ == "__main__": print(get_greatest_common_divisor(18, 45)) # 9 print(gcd_tail_recursive(23, 37)) # 1 From 50fd0586908478a59267d122811d7f6921bffb69 Mon Sep 17 00:00:00 2001 From: Acuspeedster Date: Tue, 1 Oct 2024 22:42:17 +0530 Subject: [PATCH 06/22] reduced the number of characters per line in the comments --- maths/gcd_of_n_numbers.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/maths/gcd_of_n_numbers.py b/maths/gcd_of_n_numbers.py index 9c61c91a6b56..8f524913ae73 100644 --- a/maths/gcd_of_n_numbers.py +++ b/maths/gcd_of_n_numbers.py @@ -109,15 +109,22 @@ def gcd_tail_recursive(a: int, b: int) -> int: """ Calculate the Greatest Common Divisor (GCD) using a tail-recursive approach. - This function uses the tail-recursive form of the Euclidean algorithm to calculate - the GCD of two integers `a` and `b`. The GCD is the largest integer that divides both + This function uses the tail-recursive form of + the Euclidean algorithm to calculate + the GCD of two integers `a` and `b`. The GCD + is the largest integer that divides both `a` and `b` without leaving a remainder. - Tail recursion is a form of recursion where the recursive call is the last operation - in the function. In languages that support tail call optimization, this allows the - function to be optimized by reusing the current function's stack frame for the - next call. Python, however, does not support tail call optimization, but using this - style can still help structure the recursion for better clarity. + Tail recursion is a form of recursion where the + recursive call is the last operation + in the function. In languages that + support tail call optimization, this allows the + function to be optimized by reusing the current + function's stack frame for the + next call. Python, however, does not support tail + call optimization, but using this + style can still help structure the + recursion for better clarity. Args: a (int): The first integer. From ede7215515a9e236ff5a480ca5278bf468a2ead6 Mon Sep 17 00:00:00 2001 From: Acuspeedster Date: Tue, 1 Oct 2024 22:46:40 +0530 Subject: [PATCH 07/22] removed unwanted code --- maths/gcd_of_n_numbers.py | 55 --------------------------------------- 1 file changed, 55 deletions(-) diff --git a/maths/gcd_of_n_numbers.py b/maths/gcd_of_n_numbers.py index 4a066e321477..63236c236ada 100644 --- a/maths/gcd_of_n_numbers.py +++ b/maths/gcd_of_n_numbers.py @@ -1,7 +1,6 @@ """ Gcd of N Numbers Reference: https://en.wikipedia.org/wiki/Greatest_common_divisor -Reference for tail recursion: https://www.geeksforgeeks.org/tail-recursion/ """ from collections import Counter @@ -106,59 +105,5 @@ def get_greatest_common_divisor(*numbers: int) -> int: return mult -def gcd_tail_recursive(a: int, b: int) -> int: - """ - Calculate the Greatest Common Divisor (GCD) using a tail-recursive approach. - - This function uses the tail-recursive form of - the Euclidean algorithm to calculate - the GCD of two integers `a` and `b`. The GCD - is the largest integer that divides both - `a` and `b` without leaving a remainder. - - Tail recursion is a form of recursion where the - recursive call is the last operation - in the function. In languages that - support tail call optimization, this allows the - function to be optimized by reusing the current - function's stack frame for the - next call. Python, however, does not support tail - call optimization, but using this - style can still help structure the - recursion for better clarity. - - Args: - a (int): The first integer. - b (int): The second integer. - - Returns: - int: The greatest common divisor of `a` and `b`. - - Raises: - ValueError: If both `a` and `b` are zero, as the GCD is not defined for this case. - - Example: - >>> gcd_tail_recursive(24, 40) - 8 - >>> gcd_tail_recursive(11, 37) - 1 - >>> gcd_tail_recursive(0, 5) - 5 - >>> gcd_tail_recursive(5, 0) - 5 - >>> gcd_tail_recursive(0, 0) - ValueError: GCD is not defined for both a and b being zero. - - Notes: - - gcd(a, 0) = abs(a) - - gcd(0, b) = abs(b) - - gcd(0, 0) is undefined. - """ - if b == 0: - return abs(a) - return gcd_tail_recursive(b, a % b) - - if __name__ == "__main__": print(get_greatest_common_divisor(18, 45)) # 9 - print(gcd_tail_recursive(23, 37)) # 1 From 0f939a80d2b1ebad46be86ed8f3c7a85d03c3fb5 Mon Sep 17 00:00:00 2001 From: Acuspeedster Date: Tue, 1 Oct 2024 23:07:52 +0530 Subject: [PATCH 08/22] feat: Implemented a new function to swaap numbers without dummy variable --- ...bles_without_using _temporary_variables.py | 68 +++++++++++++++++++ bit_manipulation/tempCodeRunnerFile.py | 1 + 2 files changed, 69 insertions(+) create mode 100644 bit_manipulation/swaping_two_variables_without_using _temporary_variables.py create mode 100644 bit_manipulation/tempCodeRunnerFile.py diff --git a/bit_manipulation/swaping_two_variables_without_using _temporary_variables.py b/bit_manipulation/swaping_two_variables_without_using _temporary_variables.py new file mode 100644 index 000000000000..f81b1c230349 --- /dev/null +++ b/bit_manipulation/swaping_two_variables_without_using _temporary_variables.py @@ -0,0 +1,68 @@ +def xor_swap(a: int, b: int) -> (int, int): + """ + Swap two integers using bitwise XOR + without using a temporary variable. + + This algorithm utilizes the properties + of the bitwise XOR operation to swap the values + of two integers `a` and `b` without + the use of a temporary variable. XOR swap is a + rarely used trick but showcases the power + of bit manipulation for efficient operations + at the hardware level. + + The steps involved are: + 1. `a = a ^ b` + 2. `b = a ^ b` (Now `b` holds the original value of `a`) + 3. `a = a ^ b` (Now `a` holds the original value of `b`) + + Although this technique can swap variables + without extra space, it is generally not + recommended in production code because it is + less readable than using a temporary variable. + + Args: + a (int): The first integer to be swapped. + b (int): The second integer to be swapped. + + Returns: + (int, int): The swapped values of `a` and `b`. + + Raises: + None + + Example: + >>> xor_swap(5, 10) + (10, 5) + >>> xor_swap(0, 100) + (100, 0) + >>> xor_swap(-3, 9) + (9, -3) + + Notes: + - Swapping using XOR can lead to confusion and + should generally be avoided in + favor of more readable methods. + - This algorithm does not work if both `a` and `b` + refer to the same variable. + + """ + if a == b: + print("Both values are the same; no swap needed.") + return a, b + + # print(f"Original a = {a}, b = {b}") + a = a ^ b # Step 1 + b = a ^ b # Step 2: Now b is the original value of a + a = a ^ b # Step 3: Now a is the original value of b + # print(f"Swapped a = {a}, b = {b}") + return a, b + + +if __name__ == "__main__": + import doctest + doctest.testmod() + print(xor_swap(5, 10)) # (10, 5) + print(xor_swap(0, 100)) # (100, 0) + print(xor_swap(-3, 9)) # (9, -3) + \ No newline at end of file diff --git a/bit_manipulation/tempCodeRunnerFile.py b/bit_manipulation/tempCodeRunnerFile.py new file mode 100644 index 000000000000..e5b844c5fc6a --- /dev/null +++ b/bit_manipulation/tempCodeRunnerFile.py @@ -0,0 +1 @@ + # print(f"Swapped a = {a}, b = {b}") \ No newline at end of file From 5dc67f8a1a524d112457a02a51b0c6040a224b19 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 17:39:26 +0000 Subject: [PATCH 09/22] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ...ables_without_using _temporary_variables.py | 18 +++++++++--------- bit_manipulation/tempCodeRunnerFile.py | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bit_manipulation/swaping_two_variables_without_using _temporary_variables.py b/bit_manipulation/swaping_two_variables_without_using _temporary_variables.py index f81b1c230349..7bb98f1e929e 100644 --- a/bit_manipulation/swaping_two_variables_without_using _temporary_variables.py +++ b/bit_manipulation/swaping_two_variables_without_using _temporary_variables.py @@ -4,11 +4,11 @@ def xor_swap(a: int, b: int) -> (int, int): without using a temporary variable. This algorithm utilizes the properties - of the bitwise XOR operation to swap the values + of the bitwise XOR operation to swap the values of two integers `a` and `b` without - the use of a temporary variable. XOR swap is a + the use of a temporary variable. XOR swap is a rarely used trick but showcases the power - of bit manipulation for efficient operations + of bit manipulation for efficient operations at the hardware level. The steps involved are: @@ -17,7 +17,7 @@ def xor_swap(a: int, b: int) -> (int, int): 3. `a = a ^ b` (Now `a` holds the original value of `b`) Although this technique can swap variables - without extra space, it is generally not + without extra space, it is generally not recommended in production code because it is less readable than using a temporary variable. @@ -41,7 +41,7 @@ def xor_swap(a: int, b: int) -> (int, int): Notes: - Swapping using XOR can lead to confusion and - should generally be avoided in + should generally be avoided in favor of more readable methods. - This algorithm does not work if both `a` and `b` refer to the same variable. @@ -50,7 +50,7 @@ def xor_swap(a: int, b: int) -> (int, int): if a == b: print("Both values are the same; no swap needed.") return a, b - + # print(f"Original a = {a}, b = {b}") a = a ^ b # Step 1 b = a ^ b # Step 2: Now b is the original value of a @@ -61,8 +61,8 @@ def xor_swap(a: int, b: int) -> (int, int): if __name__ == "__main__": import doctest + doctest.testmod() - print(xor_swap(5, 10)) # (10, 5) - print(xor_swap(0, 100)) # (100, 0) + print(xor_swap(5, 10)) # (10, 5) + print(xor_swap(0, 100)) # (100, 0) print(xor_swap(-3, 9)) # (9, -3) - \ No newline at end of file diff --git a/bit_manipulation/tempCodeRunnerFile.py b/bit_manipulation/tempCodeRunnerFile.py index e5b844c5fc6a..a3108748d632 100644 --- a/bit_manipulation/tempCodeRunnerFile.py +++ b/bit_manipulation/tempCodeRunnerFile.py @@ -1 +1 @@ - # print(f"Swapped a = {a}, b = {b}") \ No newline at end of file +# print(f"Swapped a = {a}, b = {b}") From b581cf5c1907bc8323880b31d120731fb8cbbd07 Mon Sep 17 00:00:00 2001 From: Acuspeedster Date: Tue, 1 Oct 2024 23:12:07 +0530 Subject: [PATCH 10/22] removed previos code --- ...bles_without_using _temporary_variables.py | 68 ------------------- 1 file changed, 68 deletions(-) delete mode 100644 bit_manipulation/swaping_two_variables_without_using _temporary_variables.py diff --git a/bit_manipulation/swaping_two_variables_without_using _temporary_variables.py b/bit_manipulation/swaping_two_variables_without_using _temporary_variables.py deleted file mode 100644 index f81b1c230349..000000000000 --- a/bit_manipulation/swaping_two_variables_without_using _temporary_variables.py +++ /dev/null @@ -1,68 +0,0 @@ -def xor_swap(a: int, b: int) -> (int, int): - """ - Swap two integers using bitwise XOR - without using a temporary variable. - - This algorithm utilizes the properties - of the bitwise XOR operation to swap the values - of two integers `a` and `b` without - the use of a temporary variable. XOR swap is a - rarely used trick but showcases the power - of bit manipulation for efficient operations - at the hardware level. - - The steps involved are: - 1. `a = a ^ b` - 2. `b = a ^ b` (Now `b` holds the original value of `a`) - 3. `a = a ^ b` (Now `a` holds the original value of `b`) - - Although this technique can swap variables - without extra space, it is generally not - recommended in production code because it is - less readable than using a temporary variable. - - Args: - a (int): The first integer to be swapped. - b (int): The second integer to be swapped. - - Returns: - (int, int): The swapped values of `a` and `b`. - - Raises: - None - - Example: - >>> xor_swap(5, 10) - (10, 5) - >>> xor_swap(0, 100) - (100, 0) - >>> xor_swap(-3, 9) - (9, -3) - - Notes: - - Swapping using XOR can lead to confusion and - should generally be avoided in - favor of more readable methods. - - This algorithm does not work if both `a` and `b` - refer to the same variable. - - """ - if a == b: - print("Both values are the same; no swap needed.") - return a, b - - # print(f"Original a = {a}, b = {b}") - a = a ^ b # Step 1 - b = a ^ b # Step 2: Now b is the original value of a - a = a ^ b # Step 3: Now a is the original value of b - # print(f"Swapped a = {a}, b = {b}") - return a, b - - -if __name__ == "__main__": - import doctest - doctest.testmod() - print(xor_swap(5, 10)) # (10, 5) - print(xor_swap(0, 100)) # (100, 0) - print(xor_swap(-3, 9)) # (9, -3) - \ No newline at end of file From 31c56459284198f8a0709d83d362d5c9904b3482 Mon Sep 17 00:00:00 2001 From: Acuspeedster Date: Wed, 2 Oct 2024 15:44:22 +0530 Subject: [PATCH 11/22] Done with the required changes --- maths/fibonacci.py | 96 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 68 insertions(+), 28 deletions(-) diff --git a/maths/fibonacci.py b/maths/fibonacci.py index 02288204da18..fb93ed36c243 100644 --- a/maths/fibonacci.py +++ b/maths/fibonacci.py @@ -18,6 +18,7 @@ from collections.abc import Iterator from math import sqrt from time import time +import numpy as np def time_func(func, *args, **kwargs): @@ -232,38 +233,77 @@ def fib_binet(n: int) -> list[int]: return [round(phi**i / sqrt_5) for i in range(n + 1)] -def fib_matrix(n: int) -> int: - # https://www.nayuki.io/page/fast-fibonacci-algorithms#:~:text= - # Summary:%20The%20two%20fast%20Fibonacci%20algorithms%20are%20matrix - def matrix_mult(a, b): - return [ - [ - a[0][0] * b[0][0] + a[0][1] * b[1][0], - a[0][0] * b[0][1] + a[0][1] * b[1][1], - ], - [ - a[1][0] * b[0][0] + a[1][1] * b[1][0], - a[1][0] * b[0][1] + a[1][1] * b[1][1], - ], - ] - - def matrix_pow(m, power): - result = [[1, 0], [0, 1]] # Identity matrix - base = m - while power: - if power % 2 == 1: - result = matrix_mult(result, base) - base = matrix_mult(base, base) - power //= 2 - return result +def matrix_mult_np(a, b): + """ + Multiplies two matrices using numpy's dot product. + + Args: + a: First matrix as a numpy array + b: Second matrix as a numpy array + + Returns: + The product of matrices a and b. + """ + return np.dot(a, b) + + +def matrix_pow_np(m, power): + """ + Raises a matrix to the power of 'power' using binary exponentiation. + + Args: + m: Matrix as a numpy array. + power: The power to which the matrix is to be raised. + + Returns: + The matrix raised to the power. + """ + result = np.identity(2, dtype=int) # Identity matrix + base = m + while power: + if power % 2 == 1: + result = matrix_mult_np(result, base) + base = matrix_mult_np(base, base) + power //= 2 + return result + +def fib_matrix_np(n: int) -> int: + """ + Calculates the n-th Fibonacci number using matrix exponentiation. + https://www.nayuki.io/page/fast-fibonacci-algorithms#:~:text= + Summary:%20The%20two%20fast%20Fibonacci%20algorithms%20are%20matrix + + Args: + n: Fibonacci sequence index + + Returns: + The n-th Fibonacci number. + + Raises: + ValueError: If n is negative. + + >>> fib_matrix_np(0) + 0 + >>> fib_matrix_np(1) + 1 + >>> fib_matrix_np(10) + 55 + >>> fib_matrix_np(-1) + Traceback (most recent call last): + ... + ValueError: n is negative + """ if n < 0: raise ValueError("n is negative") if n == 0: return 0 - m = [[1, 1], [1, 0]] - result = matrix_pow(m, n - 1) - return result[0][0] + + m = np.array([[1, 1], [1, 0]], dtype=int) + result = matrix_pow_np(m, n - 1) + return result[0, 0] + + if __name__ == "__main__": @@ -278,4 +318,4 @@ def matrix_pow(m, power): time_func(fib_memoization, num) # 0.0100 ms time_func(fib_recursive_cached, num) # 0.0153 ms time_func(fib_recursive, num) # 257.0910 ms - time_func(fib_matrix, num) # 0.0000 ms + time_func(fib_matrix_np, num) # 0.0000 ms From 6ee1a40285903d5ede52b63a447c58e70dad03ef Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 10:18:27 +0000 Subject: [PATCH 12/22] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- maths/fibonacci.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/maths/fibonacci.py b/maths/fibonacci.py index fb93ed36c243..c5bdb6ff26cf 100644 --- a/maths/fibonacci.py +++ b/maths/fibonacci.py @@ -304,8 +304,6 @@ def fib_matrix_np(n: int) -> int: return result[0, 0] - - if __name__ == "__main__": from doctest import testmod From e48a3f3e13532c8e1a9f6621c54a9cec82bbd085 Mon Sep 17 00:00:00 2001 From: Acuspeedster Date: Wed, 2 Oct 2024 16:09:12 +0530 Subject: [PATCH 13/22] Done with the required changes --- maths/fibonacci.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/maths/fibonacci.py b/maths/fibonacci.py index fb93ed36c243..6a7792fe4059 100644 --- a/maths/fibonacci.py +++ b/maths/fibonacci.py @@ -18,9 +18,12 @@ from collections.abc import Iterator from math import sqrt from time import time + import numpy as np + + def time_func(func, *args, **kwargs): """ Times the execution of a function with parameters @@ -287,12 +290,14 @@ def fib_matrix_np(n: int) -> int: 0 >>> fib_matrix_np(1) 1 + >>> fib_matrix_np(5) + 5 >>> fib_matrix_np(10) 55 >>> fib_matrix_np(-1) Traceback (most recent call last): ... - ValueError: n is negative + Exception: n is negative """ if n < 0: raise ValueError("n is negative") From f82bd8c3735967af24e064e4601cae9725b40c17 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 10:40:25 +0000 Subject: [PATCH 14/22] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- maths/fibonacci.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/maths/fibonacci.py b/maths/fibonacci.py index 302e8a2aa2ae..62dde20385b1 100644 --- a/maths/fibonacci.py +++ b/maths/fibonacci.py @@ -22,8 +22,6 @@ import numpy as np - - def time_func(func, *args, **kwargs): """ Times the execution of a function with parameters From 8bf138985ee7941b5a741fb9661476dc51f01273 Mon Sep 17 00:00:00 2001 From: Acuspeedster Date: Wed, 2 Oct 2024 16:20:21 +0530 Subject: [PATCH 15/22] Done with the required changes --- maths/fibonacci.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/maths/fibonacci.py b/maths/fibonacci.py index 302e8a2aa2ae..3f3a6366c348 100644 --- a/maths/fibonacci.py +++ b/maths/fibonacci.py @@ -250,7 +250,7 @@ def matrix_mult_np(a, b): return np.dot(a, b) -def matrix_pow_np(m, power): +def matrix_pow_np(m: int, power: int) -> int: """ Raises a matrix to the power of 'power' using binary exponentiation. @@ -294,10 +294,6 @@ def fib_matrix_np(n: int) -> int: 5 >>> fib_matrix_np(10) 55 - >>> fib_matrix_np(-1) - Traceback (most recent call last): - ... - Exception: n is negative """ if n < 0: raise ValueError("n is negative") From 9ffd634a7154e9e45e593ea34c421b2422ea4c43 Mon Sep 17 00:00:00 2001 From: Acuspeedster Date: Wed, 2 Oct 2024 16:25:49 +0530 Subject: [PATCH 16/22] Done with the required changes --- maths/fibonacci.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/maths/fibonacci.py b/maths/fibonacci.py index 9581a586a7dc..3f06e5b55e4d 100644 --- a/maths/fibonacci.py +++ b/maths/fibonacci.py @@ -292,6 +292,10 @@ def fib_matrix_np(n: int) -> int: 5 >>> fib_matrix_np(10) 55 + >>> fib_matrix_np(-1) + Traceback (most recent call last): + ... + ValueError: n is negative """ if n < 0: raise ValueError("n is negative") @@ -300,7 +304,7 @@ def fib_matrix_np(n: int) -> int: m = np.array([[1, 1], [1, 0]], dtype=int) result = matrix_pow_np(m, n - 1) - return result[0, 0] + return int(result[0, 0]) if __name__ == "__main__": From 4e2456f51869c6eb942d5faf294f325d6bbd7096 Mon Sep 17 00:00:00 2001 From: Acuspeedster Date: Wed, 2 Oct 2024 16:35:56 +0530 Subject: [PATCH 17/22] Done with the required changes --- maths/fibonacci.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/maths/fibonacci.py b/maths/fibonacci.py index 3f06e5b55e4d..99aabaf7f1de 100644 --- a/maths/fibonacci.py +++ b/maths/fibonacci.py @@ -20,6 +20,7 @@ from time import time import numpy as np +from numpy import ndarray def time_func(func, *args, **kwargs): @@ -247,8 +248,7 @@ def matrix_mult_np(a, b): """ return np.dot(a, b) - -def matrix_pow_np(m: int, power: int) -> int: +def matrix_pow_np(m: ndarray, power: int) -> ndarray: """ Raises a matrix to the power of 'power' using binary exponentiation. @@ -259,12 +259,12 @@ def matrix_pow_np(m: int, power: int) -> int: Returns: The matrix raised to the power. """ - result = np.identity(2, dtype=int) # Identity matrix + result = np.array([[1, 0], [0, 1]], dtype=int) # Identity matrix base = m while power: if power % 2 == 1: - result = matrix_mult_np(result, base) - base = matrix_mult_np(base, base) + result = np.dot(result, base) + base = np.dot(base, base) power //= 2 return result From 07ed6b9c4e537f4620b90d6cfec7f317c235bf3e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 11:07:22 +0000 Subject: [PATCH 18/22] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- maths/fibonacci.py | 1 + 1 file changed, 1 insertion(+) diff --git a/maths/fibonacci.py b/maths/fibonacci.py index 99aabaf7f1de..7d10586196b0 100644 --- a/maths/fibonacci.py +++ b/maths/fibonacci.py @@ -248,6 +248,7 @@ def matrix_mult_np(a, b): """ return np.dot(a, b) + def matrix_pow_np(m: ndarray, power: int) -> ndarray: """ Raises a matrix to the power of 'power' using binary exponentiation. From 955c7929319e266baa55cb7044ba8d580b72acd7 Mon Sep 17 00:00:00 2001 From: ARNAV RAJ <126798788+Acuspeedster@users.noreply.github.com> Date: Fri, 4 Oct 2024 15:44:01 +0530 Subject: [PATCH 19/22] Update maths/fibonacci.py Co-authored-by: Tianyi Zheng --- maths/fibonacci.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/maths/fibonacci.py b/maths/fibonacci.py index 7d10586196b0..b5bd0ef2f83f 100644 --- a/maths/fibonacci.py +++ b/maths/fibonacci.py @@ -235,18 +235,6 @@ def fib_binet(n: int) -> list[int]: return [round(phi**i / sqrt_5) for i in range(n + 1)] -def matrix_mult_np(a, b): - """ - Multiplies two matrices using numpy's dot product. - - Args: - a: First matrix as a numpy array - b: Second matrix as a numpy array - - Returns: - The product of matrices a and b. - """ - return np.dot(a, b) def matrix_pow_np(m: ndarray, power: int) -> ndarray: From 70dd92de617a2b73186928872e3e1e9e4b65286c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2024 10:15:43 +0000 Subject: [PATCH 20/22] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- maths/fibonacci.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/maths/fibonacci.py b/maths/fibonacci.py index b5bd0ef2f83f..c53ddf3e33c6 100644 --- a/maths/fibonacci.py +++ b/maths/fibonacci.py @@ -235,8 +235,6 @@ def fib_binet(n: int) -> list[int]: return [round(phi**i / sqrt_5) for i in range(n + 1)] - - def matrix_pow_np(m: ndarray, power: int) -> ndarray: """ Raises a matrix to the power of 'power' using binary exponentiation. From 19091434724f003c7e359e0dd39c56f6736865be Mon Sep 17 00:00:00 2001 From: Acuspeedster Date: Fri, 4 Oct 2024 15:48:21 +0530 Subject: [PATCH 21/22] Done with the required changes --- maths/fibonacci.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/maths/fibonacci.py b/maths/fibonacci.py index b5bd0ef2f83f..2a68c36f141f 100644 --- a/maths/fibonacci.py +++ b/maths/fibonacci.py @@ -247,6 +247,27 @@ def matrix_pow_np(m: ndarray, power: int) -> ndarray: Returns: The matrix raised to the power. + + Raises: + ValueError: If power is negative. + + >>> m = np.array([[1, 1], [1, 0]], dtype=int) + >>> matrix_pow_np(m, 0) # Identity matrix when raised to the power of 0 + array([[1, 0], + [0, 1]]) + + >>> matrix_pow_np(m, 1) # Same matrix when raised to the power of 1 + array([[1, 1], + [1, 0]]) + + >>> matrix_pow_np(m, 5) + array([[8, 5], + [5, 3]]) + + >>> matrix_pow_np(m, -1) + Traceback (most recent call last): + ... + ValueError: power is negative """ result = np.array([[1, 0], [0, 1]], dtype=int) # Identity matrix base = m From b7db4ca56d99eb0244b1ebc65f63fa72bfbee3eb Mon Sep 17 00:00:00 2001 From: Acuspeedster Date: Fri, 4 Oct 2024 15:53:39 +0530 Subject: [PATCH 22/22] Done with the required changes --- maths/fibonacci.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maths/fibonacci.py b/maths/fibonacci.py index 113a88d962b7..cb7d0c4458c5 100644 --- a/maths/fibonacci.py +++ b/maths/fibonacci.py @@ -267,7 +267,7 @@ def matrix_pow_np(m: ndarray, power: int) -> ndarray: ... ValueError: power is negative """ - result = np.array([[1, 0], [0, 1]], dtype=int) # Identity matrix + result = np.array([[1, 0], [0, 1]], dtype=int) # Identity Matrix base = m while power: if power % 2 == 1: