From 53a30c4f51b90836df698c21a321738e8c337133 Mon Sep 17 00:00:00 2001 From: Akibuzzaman Akib Date: Tue, 14 Apr 2026 12:58:26 +0000 Subject: [PATCH 1/4] maths: add O(log n) Fibonacci via matrix exponentiation - Implements fibonacci(n) using fast matrix exponentiation - Time complexity: O(log n), Space complexity: O(log n) - Full docstring with Wikipedia reference - Type hints on all parameters and return values - Doctests for edge cases: fib(0), fib(1), fib(10), fib(20), fib(50) - Raises ValueError for negative input --- maths/fibonacci_fast.py | 105 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 maths/fibonacci_fast.py diff --git a/maths/fibonacci_fast.py b/maths/fibonacci_fast.py new file mode 100644 index 000000000000..17d58504448d --- /dev/null +++ b/maths/fibonacci_fast.py @@ -0,0 +1,105 @@ +""" +Fibonacci sequence via matrix exponentiation — O(log n) time complexity. + +The standard recursive Fibonacci runs in O(2^n) time. Using matrix exponentiation +we can compute the n-th Fibonacci number in O(log n) multiplications. + +The key identity is: + | F(n+1) F(n) | | 1 1 | ^ n + | F(n) F(n-1) | = | 1 0 | + +So F(n) = (M^n)[0][1] where M = [[1, 1], [1, 0]]. + +References: + - https://en.wikipedia.org/wiki/Fibonacci_number#Matrix_form +""" + + +def _mat_mul( + a: list[list[int]], b: list[list[int]] +) -> list[list[int]]: + """Multiply two 2×2 integer matrices. + + >>> _mat_mul([[1, 1], [1, 0]], [[1, 0], [0, 1]]) + [[1, 1], [1, 0]] + """ + 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 _mat_pow(matrix: list[list[int]], power: int) -> list[list[int]]: + """Raise a 2×2 integer matrix to a non-negative integer power using + fast exponentiation (repeated squaring). + + :param matrix: A 2×2 matrix represented as a list of lists. + :param power: Non-negative integer exponent. + :return: matrix ** power + + >>> _mat_pow([[1, 1], [1, 0]], 0) + [[1, 0], [0, 1]] + >>> _mat_pow([[1, 1], [1, 0]], 1) + [[1, 1], [1, 0]] + >>> _mat_pow([[1, 1], [1, 0]], 2) + [[2, 1], [1, 1]] + """ + # Identity matrix + result: list[list[int]] = [[1, 0], [0, 1]] + while power: + if power % 2 == 1: + result = _mat_mul(result, matrix) + matrix = _mat_mul(matrix, matrix) + power //= 2 + return result + + +def fibonacci(n: int) -> int: + """Return the n-th Fibonacci number using matrix exponentiation. + + Time complexity: O(log n) + Space complexity: O(log n) due to the call stack of _mat_pow + + :param n: Non-negative integer index into the Fibonacci sequence + (0-indexed: F(0)=0, F(1)=1, F(2)=1, ...). + :raises ValueError: If *n* is negative. + :return: The n-th Fibonacci number. + + >>> fibonacci(0) + 0 + >>> fibonacci(1) + 1 + >>> fibonacci(2) + 1 + >>> fibonacci(10) + 55 + >>> fibonacci(20) + 6765 + >>> fibonacci(50) + 12586269025 + >>> fibonacci(-1) + Traceback (most recent call last): + ... + ValueError: fibonacci() only accepts non-negative integers + """ + if n < 0: + raise ValueError("fibonacci() only accepts non-negative integers") + if n == 0: + return 0 + m: list[list[int]] = [[1, 1], [1, 0]] + return _mat_pow(m, n)[0][1] + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + for i in range(15): + print(f"fibonacci({i}) = {fibonacci(i)}") From 66919bded071f489aaa2c8e5de77b12f4576f4c4 Mon Sep 17 00:00:00 2001 From: Akibuzzaman Akib Date: Tue, 14 Apr 2026 13:04:45 +0000 Subject: [PATCH 2/4] =?UTF-8?q?style:=20fix=20ruff=20linting=20=E2=80=94?= =?UTF-8?q?=20replace=20ambiguous=20multiplication=20sign,=20apply=20forma?= =?UTF-8?q?tting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- maths/fibonacci_fast.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/maths/fibonacci_fast.py b/maths/fibonacci_fast.py index 17d58504448d..3cd5964ea1f1 100644 --- a/maths/fibonacci_fast.py +++ b/maths/fibonacci_fast.py @@ -15,10 +15,8 @@ """ -def _mat_mul( - a: list[list[int]], b: list[list[int]] -) -> list[list[int]]: - """Multiply two 2×2 integer matrices. +def _mat_mul(a: list[list[int]], b: list[list[int]]) -> list[list[int]]: + """Multiply two 2x2 integer matrices. >>> _mat_mul([[1, 1], [1, 0]], [[1, 0], [0, 1]]) [[1, 1], [1, 0]] @@ -36,10 +34,10 @@ def _mat_mul( def _mat_pow(matrix: list[list[int]], power: int) -> list[list[int]]: - """Raise a 2×2 integer matrix to a non-negative integer power using + """Raise a 2x2 integer matrix to a non-negative integer power using fast exponentiation (repeated squaring). - :param matrix: A 2×2 matrix represented as a list of lists. + :param matrix: A 2x2 matrix represented as a list of lists. :param power: Non-negative integer exponent. :return: matrix ** power From 4280d0e15ff384b430d71e09b90f56ec6a8e1643 Mon Sep 17 00:00:00 2001 From: AKIBUZZAMAN AKIB Date: Wed, 15 Apr 2026 07:27:15 +0600 Subject: [PATCH 3/4] fix: use descriptive parameter names (mat_a, mat_b, index) per review --- maths/fibonacci_fast.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/maths/fibonacci_fast.py b/maths/fibonacci_fast.py index 3cd5964ea1f1..3dfbd1e5c1dc 100644 --- a/maths/fibonacci_fast.py +++ b/maths/fibonacci_fast.py @@ -15,7 +15,9 @@ """ -def _mat_mul(a: list[list[int]], b: list[list[int]]) -> list[list[int]]: +def _mat_mul( + mat_a: list[list[int]], mat_b: list[list[int]] +) -> list[list[int]]: """Multiply two 2x2 integer matrices. >>> _mat_mul([[1, 1], [1, 0]], [[1, 0], [0, 1]]) @@ -23,12 +25,12 @@ def _mat_mul(a: list[list[int]], b: list[list[int]]) -> list[list[int]]: """ 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], + mat_a[0][0] * mat_b[0][0] + mat_a[0][1] * mat_b[1][0], + mat_a[0][0] * mat_b[0][1] + mat_a[0][1] * mat_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], + mat_a[1][0] * mat_b[0][0] + mat_a[1][1] * mat_b[1][0], + mat_a[1][0] * mat_b[0][1] + mat_a[1][1] * mat_b[1][1], ], ] @@ -58,16 +60,16 @@ def _mat_pow(matrix: list[list[int]], power: int) -> list[list[int]]: return result -def fibonacci(n: int) -> int: +def fibonacci(index: int) -> int: """Return the n-th Fibonacci number using matrix exponentiation. Time complexity: O(log n) Space complexity: O(log n) due to the call stack of _mat_pow - :param n: Non-negative integer index into the Fibonacci sequence - (0-indexed: F(0)=0, F(1)=1, F(2)=1, ...). - :raises ValueError: If *n* is negative. - :return: The n-th Fibonacci number. + :param index: Non-negative integer index into the Fibonacci sequence + (0-indexed: F(0)=0, F(1)=1, F(2)=1, ...). + :raises ValueError: If *index* is negative. + :return: The Fibonacci number at the given index. >>> fibonacci(0) 0 @@ -86,12 +88,12 @@ def fibonacci(n: int) -> int: ... ValueError: fibonacci() only accepts non-negative integers """ - if n < 0: + if index < 0: raise ValueError("fibonacci() only accepts non-negative integers") - if n == 0: + if index == 0: return 0 - m: list[list[int]] = [[1, 1], [1, 0]] - return _mat_pow(m, n)[0][1] + mat: list[list[int]] = [[1, 1], [1, 0]] + return _mat_pow(mat, index)[0][1] if __name__ == "__main__": From 382ee4435a6be23dfaafb79597f70e85971e5632 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 01:27:53 +0000 Subject: [PATCH 4/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- maths/fibonacci_fast.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/maths/fibonacci_fast.py b/maths/fibonacci_fast.py index 3dfbd1e5c1dc..19d5b2afc60c 100644 --- a/maths/fibonacci_fast.py +++ b/maths/fibonacci_fast.py @@ -15,9 +15,7 @@ """ -def _mat_mul( - mat_a: list[list[int]], mat_b: list[list[int]] -) -> list[list[int]]: +def _mat_mul(mat_a: list[list[int]], mat_b: list[list[int]]) -> list[list[int]]: """Multiply two 2x2 integer matrices. >>> _mat_mul([[1, 1], [1, 0]], [[1, 0], [0, 1]])