mirror of
https://hub.njuu.cf/TheAlgorithms/Python.git
synced 2023-10-11 13:06:12 +08:00
Merge maths/fibonacci.py and maths/fibonacci_sequence_recursion.py (#5738)
* Rewrite parts of Vector and Matrix methods * Refactor determinant method and add unit tests Refactor determinant method to create separate minor and cofactor methods. Add respective unit tests for new methods. Rename methods using snake case to follow Python naming conventions. * Reorganize Vector and Matrix methods * Update linear_algebra/README.md Co-authored-by: John Law <johnlaw.po@gmail.com> * Fix punctuation and wording * Apply suggestions from code review Co-authored-by: John Law <johnlaw.po@gmail.com> * Deduplicate euclidean length method for Vector * Add more unit tests for Euclidean length method * Fix bug in unit test for euclidean_length * Remove old comments for magnitude method * Rewrite maths/fibonacci.py * Rewrite timer and add unit tests * Fix typos in fib_binet unit tests * Fix typos in fib_binet unit tests * Clean main method * Merge fibonacci.py and fibonacci_sequence_recursion.py * Fix fib_binet unit test Co-authored-by: John Law <johnlaw.po@gmail.com>
This commit is contained in:
parent
99cf2cc1c5
commit
06ab650e08
@ -1,130 +1,130 @@
|
|||||||
# fibonacci.py
|
# fibonacci.py
|
||||||
"""
|
"""
|
||||||
1. Calculates the iterative fibonacci sequence
|
Calculates the Fibonacci sequence using iteration, recursion, and a simplified
|
||||||
|
form of Binet's formula
|
||||||
2. Calculates the fibonacci sequence with a formula
|
|
||||||
an = [ Phin - (phi)n ]/Sqrt[5]
|
NOTE 1: the iterative and recursive functions are more accurate than the Binet's
|
||||||
reference-->Su, Francis E., et al. "Fibonacci Number Formula." Math Fun Facts.
|
formula function because the iterative function doesn't use floats
|
||||||
<http://www.math.hmc.edu/funfacts>
|
|
||||||
"""
|
NOTE 2: the Binet's formula function is much more limited in the size of inputs
|
||||||
import functools
|
that it can handle due to the size limitations of Python floats
|
||||||
import math
|
"""
|
||||||
import time
|
|
||||||
from decimal import Decimal, getcontext
|
from math import sqrt
|
||||||
|
from time import time
|
||||||
getcontext().prec = 100
|
|
||||||
|
|
||||||
|
def time_func(func, *args, **kwargs):
|
||||||
def timer_decorator(func):
|
"""
|
||||||
@functools.wraps(func)
|
Times the execution of a function with parameters
|
||||||
def timer_wrapper(*args, **kwargs):
|
"""
|
||||||
start = time.time()
|
start = time()
|
||||||
func(*args, **kwargs)
|
output = func(*args, **kwargs)
|
||||||
end = time.time()
|
end = time()
|
||||||
if int(end - start) > 0:
|
if int(end - start) > 0:
|
||||||
print(f"Run time for {func.__name__}: {(end - start):0.2f}s")
|
print(f"{func.__name__} runtime: {(end - start):0.4f} s")
|
||||||
else:
|
else:
|
||||||
print(f"Run time for {func.__name__}: {(end - start)*1000:0.2f}ms")
|
print(f"{func.__name__} runtime: {(end - start) * 1000:0.4f} ms")
|
||||||
return func(*args, **kwargs)
|
return output
|
||||||
|
|
||||||
return timer_wrapper
|
|
||||||
|
def fib_iterative(n: int) -> list[int]:
|
||||||
|
"""
|
||||||
# define Python user-defined exceptions
|
Calculates the first n (0-indexed) Fibonacci numbers using iteration
|
||||||
class Error(Exception):
|
>>> fib_iterative(0)
|
||||||
"""Base class for other exceptions"""
|
[0]
|
||||||
|
>>> fib_iterative(1)
|
||||||
|
[0, 1]
|
||||||
class ValueTooLargeError(Error):
|
>>> fib_iterative(5)
|
||||||
"""Raised when the input value is too large"""
|
[0, 1, 1, 2, 3, 5]
|
||||||
|
>>> fib_iterative(10)
|
||||||
|
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
|
||||||
class ValueTooSmallError(Error):
|
>>> fib_iterative(-1)
|
||||||
"""Raised when the input value is not greater than one"""
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
Exception: n is negative
|
||||||
class ValueLessThanZero(Error):
|
"""
|
||||||
"""Raised when the input value is less than zero"""
|
if n < 0:
|
||||||
|
raise Exception("n is negative")
|
||||||
|
if n == 0:
|
||||||
def _check_number_input(n, min_thresh, max_thresh=None):
|
return [0]
|
||||||
"""
|
fib = [0, 1]
|
||||||
:param n: single integer
|
for _ in range(n - 1):
|
||||||
:type n: int
|
fib.append(fib[-1] + fib[-2])
|
||||||
:param min_thresh: min threshold, single integer
|
return fib
|
||||||
:type min_thresh: int
|
|
||||||
:param max_thresh: max threshold, single integer
|
|
||||||
:type max_thresh: int
|
def fib_recursive(n: int) -> list[int]:
|
||||||
:return: boolean
|
"""
|
||||||
"""
|
Calculates the first n (0-indexed) Fibonacci numbers using recursion
|
||||||
try:
|
>>> fib_iterative(0)
|
||||||
if n >= min_thresh and max_thresh is None:
|
[0]
|
||||||
return True
|
>>> fib_iterative(1)
|
||||||
elif min_thresh <= n <= max_thresh:
|
[0, 1]
|
||||||
return True
|
>>> fib_iterative(5)
|
||||||
elif n < 0:
|
[0, 1, 1, 2, 3, 5]
|
||||||
raise ValueLessThanZero
|
>>> fib_iterative(10)
|
||||||
elif n < min_thresh:
|
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
|
||||||
raise ValueTooSmallError
|
>>> fib_iterative(-1)
|
||||||
elif n > max_thresh:
|
Traceback (most recent call last):
|
||||||
raise ValueTooLargeError
|
...
|
||||||
except ValueLessThanZero:
|
Exception: n is negative
|
||||||
print("Incorrect Input: number must not be less than 0")
|
"""
|
||||||
except ValueTooSmallError:
|
|
||||||
print(
|
def fib_recursive_term(i: int) -> int:
|
||||||
f"Incorrect Input: input number must be > {min_thresh} for the recursive "
|
"""
|
||||||
"calculation"
|
Calculates the i-th (0-indexed) Fibonacci number using recursion
|
||||||
)
|
"""
|
||||||
except ValueTooLargeError:
|
if i < 0:
|
||||||
print(
|
raise Exception("n is negative")
|
||||||
f"Incorrect Input: input number must be < {max_thresh} for the recursive "
|
if i < 2:
|
||||||
"calculation"
|
return i
|
||||||
)
|
return fib_recursive_term(i - 1) + fib_recursive_term(i - 2)
|
||||||
return False
|
|
||||||
|
if n < 0:
|
||||||
|
raise Exception("n is negative")
|
||||||
@timer_decorator
|
return [fib_recursive_term(i) for i in range(n + 1)]
|
||||||
def fib_iterative(n):
|
|
||||||
"""
|
|
||||||
:param n: calculate Fibonacci to the nth integer
|
def fib_binet(n: int) -> list[int]:
|
||||||
:type n:int
|
"""
|
||||||
:return: Fibonacci sequence as a list
|
Calculates the first n (0-indexed) Fibonacci numbers using a simplified form
|
||||||
"""
|
of Binet's formula:
|
||||||
n = int(n)
|
https://en.m.wikipedia.org/wiki/Fibonacci_number#Computation_by_rounding
|
||||||
if _check_number_input(n, 2):
|
|
||||||
seq_out = [0, 1]
|
NOTE 1: this function diverges from fib_iterative at around n = 71, likely
|
||||||
a, b = 0, 1
|
due to compounding floating-point arithmetic errors
|
||||||
for _ in range(n - len(seq_out)):
|
|
||||||
a, b = b, a + b
|
NOTE 2: this function overflows on n >= 1475 because of the size limitations
|
||||||
seq_out.append(b)
|
of Python floats
|
||||||
return seq_out
|
>>> fib_binet(0)
|
||||||
|
[0]
|
||||||
|
>>> fib_binet(1)
|
||||||
@timer_decorator
|
[0, 1]
|
||||||
def fib_formula(n):
|
>>> fib_binet(5)
|
||||||
"""
|
[0, 1, 1, 2, 3, 5]
|
||||||
:param n: calculate Fibonacci to the nth integer
|
>>> fib_binet(10)
|
||||||
:type n:int
|
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
|
||||||
:return: Fibonacci sequence as a list
|
>>> fib_binet(-1)
|
||||||
"""
|
Traceback (most recent call last):
|
||||||
seq_out = [0, 1]
|
...
|
||||||
n = int(n)
|
Exception: n is negative
|
||||||
if _check_number_input(n, 2, 1000000):
|
>>> fib_binet(1475)
|
||||||
sqrt = Decimal(math.sqrt(5))
|
Traceback (most recent call last):
|
||||||
phi_1 = Decimal(1 + sqrt) / Decimal(2)
|
...
|
||||||
phi_2 = Decimal(1 - sqrt) / Decimal(2)
|
Exception: n is too large
|
||||||
for i in range(2, n):
|
"""
|
||||||
temp_out = ((phi_1 ** Decimal(i)) - (phi_2 ** Decimal(i))) * (
|
if n < 0:
|
||||||
Decimal(sqrt) ** Decimal(-1)
|
raise Exception("n is negative")
|
||||||
)
|
if n >= 1475:
|
||||||
seq_out.append(int(temp_out))
|
raise Exception("n is too large")
|
||||||
return seq_out
|
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
|
|
||||||
# print(f'{fib_recursive(num)}\n')
|
if __name__ == "__main__":
|
||||||
# print(f'{fib_iterative(num)}\n')
|
num = 20
|
||||||
# print(f'{fib_formula(num)}\n')
|
time_func(fib_iterative, num)
|
||||||
fib_iterative(num)
|
time_func(fib_recursive, num)
|
||||||
fib_formula(num)
|
time_func(fib_binet, num)
|
||||||
|
@ -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()
|
|
Loading…
Reference in New Issue
Block a user