Repository: TheAlgorithms/Python

Issue:
Merge Fibonacci files
As shown by issues #5665 and #5733, there are apparently multiple files for calculating the Fibonacci sequence, namely `dynamic_programming/fibonacci.py` and `maths/fibonacci.py`. There's also a third file, `maths/fibonacci_sequence_recursion.py`, that's not mentioned in the previous issues, and there might be more that I haven't spotted yet. Each of these files provides different algorithms for calculating the sequence.

I feel like some if not all of these files should be combined into one file. It'd not only be more convenient, but it'd also be easier to compare the algorithms against one another.


Discussion:

Resolving patch:
diff --git a/maths/fibonacci.py b/maths/fibonacci.py
--- a/maths/fibonacci.py
+++ b/maths/fibonacci.py
@@ -1,130 +1,130 @@
-# fibonacci.py
-"""
-1. Calculates the iterative fibonacci sequence
-
-2. Calculates the fibonacci sequence with a formula
-    an = [ Phin - (phi)n ]/Sqrt[5]
-    reference-->Su, Francis E., et al. "Fibonacci Number Formula." Math Fun Facts.
-    <http://www.math.hmc.edu/funfacts>
-"""
-import functools
-import math
-import time
-from decimal import Decimal, getcontext
-
-getcontext().prec = 100
-
-
-def timer_decorator(func):
-    @functools.wraps(func)
-    def timer_wrapper(*args, **kwargs):
-        start = time.time()
-        func(*args, **kwargs)
-        end = time.time()
-        if int(end - start) > 0:
-            print(f"Run time for {func.__name__}: {(end - start):0.2f}s")
-        else:
-            print(f"Run time for {func.__name__}: {(end - start)*1000:0.2f}ms")
-        return func(*args, **kwargs)
-
-    return timer_wrapper
-
-
-# define Python user-defined exceptions
-class Error(Exception):
-    """Base class for other exceptions"""
-
-
-class ValueTooLargeError(Error):
-    """Raised when the input value is too large"""
-
-
-class ValueTooSmallError(Error):
-    """Raised when the input value is not greater than one"""
-
-
-class ValueLessThanZero(Error):
-    """Raised when the input value is less than zero"""
-
-
-def _check_number_input(n, min_thresh, max_thresh=None):
-    """
-    :param n: single integer
-    :type n: int
-    :param min_thresh: min threshold, single integer
-    :type min_thresh: int
-    :param max_thresh: max threshold, single integer
-    :type max_thresh: int
-    :return: boolean
-    """
-    try:
-        if n >= min_thresh and max_thresh is None:
-            return True
-        elif min_thresh <= n <= max_thresh:
-            return True
-        elif n < 0:
-            raise ValueLessThanZero
-        elif n < min_thresh:
-            raise ValueTooSmallError
-        elif n > max_thresh:
-            raise ValueTooLargeError
-    except ValueLessThanZero:
-        print("Incorrect Input: number must not be less than 0")
-    except ValueTooSmallError:
-        print(
-            f"Incorrect Input: input number must be > {min_thresh} for the recursive "
-            "calculation"
-        )
-    except ValueTooLargeError:
-        print(
-            f"Incorrect Input: input number must be < {max_thresh} for the recursive "
-            "calculation"
-        )
-    return False
-
-
-@timer_decorator
-def fib_iterative(n):
-    """
-    :param n: calculate Fibonacci to the nth integer
-    :type n:int
-    :return: Fibonacci sequence as a list
-    """
-    n = int(n)
-    if _check_number_input(n, 2):
-        seq_out = [0, 1]
-        a, b = 0, 1
-        for _ in range(n - len(seq_out)):
-            a, b = b, a + b
-            seq_out.append(b)
-        return seq_out
-
-
-@timer_decorator
-def fib_formula(n):
-    """
-    :param n: calculate Fibonacci to the nth integer
-    :type n:int
-    :return: Fibonacci sequence as a list
-    """
-    seq_out = [0, 1]
-    n = int(n)
-    if _check_number_input(n, 2, 1000000):
-        sqrt = Decimal(math.sqrt(5))
-        phi_1 = Decimal(1 + sqrt) / Decimal(2)
-        phi_2 = Decimal(1 - sqrt) / Decimal(2)
-        for i in range(2, n):
-            temp_out = ((phi_1 ** Decimal(i)) - (phi_2 ** Decimal(i))) * (
-                Decimal(sqrt) ** Decimal(-1)
-            )
-            seq_out.append(int(temp_out))
-        return seq_out
-
-
-if __name__ == "__main__":
-    num = 20
-    # print(f'{fib_recursive(num)}\n')
-    # print(f'{fib_iterative(num)}\n')
-    # print(f'{fib_formula(num)}\n')
-    fib_iterative(num)
-    fib_formula(num)
+# fibonacci.py
+"""
+Calculates the Fibonacci sequence using iteration, recursion, and a simplified
+form of Binet's formula
+
+NOTE 1: the iterative and recursive functions are more accurate than the Binet's
+formula function because the iterative function doesn't use floats
+
+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
+"""
+
+from math import sqrt
+from time import time
+
+
+def time_func(func, *args, **kwargs):
+    """
+    Times the execution of a function with parameters
+    """
+    start = time()
+    output = func(*args, **kwargs)
+    end = time()
+    if int(end - start) > 0:
+        print(f"{func.__name__} runtime: {(end - start):0.4f} s")
+    else:
+        print(f"{func.__name__} runtime: {(end - start) * 1000:0.4f} ms")
+    return output
+
+
+def fib_iterative(n: int) -> list[int]:
+    """
+    Calculates the first n (0-indexed) Fibonacci numbers using iteration
+    >>> fib_iterative(0)
+    [0]
+    >>> fib_iterative(1)
+    [0, 1]
+    >>> fib_iterative(5)
+    [0, 1, 1, 2, 3, 5]
+    >>> fib_iterative(10)
+    [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
+    >>> fib_iterative(-1)
+    Traceback (most recent call last):
+    ...
+    Exception: n is negative
+    """
+    if n < 0:
+        raise Exception("n is negative")
+    if n == 0:
+        return [0]
+    fib = [0, 1]
+    for _ in range(n - 1):
+        fib.append(fib[-1] + fib[-2])
+    return fib
+
+
+def fib_recursive(n: int) -> list[int]:
+    """
+    Calculates the first n (0-indexed) Fibonacci numbers using recursion
+    >>> fib_iterative(0)
+    [0]
+    >>> fib_iterative(1)
+    [0, 1]
+    >>> fib_iterative(5)
+    [0, 1, 1, 2, 3, 5]
+    >>> fib_iterative(10)
+    [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
+    >>> fib_iterative(-1)
+    Traceback (most recent call last):
+    ...
+    Exception: n is negative
+    """
+
+    def fib_recursive_term(i: int) -> int:
+        """
+        Calculates the i-th (0-indexed) Fibonacci number using recursion
+        """
+        if i < 0:
+            raise Exception("n is negative")
+        if i < 2:
+            return i
+        return fib_recursive_term(i - 1) + fib_recursive_term(i - 2)
+
+    if n < 0:
+        raise Exception("n is negative")
+    return [fib_recursive_term(i) for i in range(n + 1)]
+
+
+def fib_binet(n: int) -> list[int]:
+    """
+    Calculates the first n (0-indexed) Fibonacci numbers using a simplified form
+    of Binet's formula:
+    https://en.m.wikipedia.org/wiki/Fibonacci_number#Computation_by_rounding
+
+    NOTE 1: this function diverges from fib_iterative at around n = 71, likely
+    due to compounding floating-point arithmetic errors
+
+    NOTE 2: this function overflows on n >= 1475 because of the size limitations
+    of Python floats
+    >>> fib_binet(0)
+    [0]
+    >>> fib_binet(1)
+    [0, 1]
+    >>> fib_binet(5)
+    [0, 1, 1, 2, 3, 5]
+    >>> fib_binet(10)
+    [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
+    >>> fib_binet(-1)
+    Traceback (most recent call last):
+    ...
+    Exception: n is negative
+    >>> fib_binet(1475)
+    Traceback (most recent call last):
+    ...
+    Exception: n is too large
+    """
+    if n < 0:
+        raise Exception("n is negative")
+    if n >= 1475:
+        raise Exception("n is too large")
+    sqrt_5 = sqrt(5)
+    phi = (1 + sqrt_5) / 2
+    return [round(phi ** i / sqrt_5) for i in range(n + 1)]
+
+
+if __name__ == "__main__":
+    num = 20
+    time_func(fib_iterative, num)
+    time_func(fib_recursive, num)
+    time_func(fib_binet, num)
diff --git a/maths/fibonacci_sequence_recursion.py b/maths/fibonacci_sequence_recursion.py
deleted file mode 100644
--- a/maths/fibonacci_sequence_recursion.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Fibonacci Sequence Using Recursion
-
-
-def recur_fibo(n: int) -> int:
-    """
-    >>> [recur_fibo(i) for i in range(12)]
-    [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
-    """
-    return n if n <= 1 else recur_fibo(n - 1) + recur_fibo(n - 2)
-
-
-def main() -> None:
-    limit = int(input("How many terms to include in fibonacci series: "))
-    if limit > 0:
-        print(f"The first {limit} terms of the fibonacci series are as follows:")
-        print([recur_fibo(n) for n in range(limit)])
-    else:
-        print("Please enter a positive integer: ")
-
-
-if __name__ == "__main__":
-    main()


Test patch: