Skip to content

Commit 06ab650

Browse files
tianyizheng02poyea
andauthored
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 <[email protected]> * Fix punctuation and wording * Apply suggestions from code review Co-authored-by: John Law <[email protected]> * 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 <[email protected]>
1 parent 99cf2cc commit 06ab650

File tree

2 files changed

+130
-152
lines changed

2 files changed

+130
-152
lines changed

maths/fibonacci.py

+130-130
Original file line numberDiff line numberDiff line change
@@ -1,130 +1,130 @@
1-
# fibonacci.py
2-
"""
3-
1. Calculates the iterative fibonacci sequence
4-
5-
2. Calculates the fibonacci sequence with a formula
6-
an = [ Phin - (phi)n ]/Sqrt[5]
7-
reference-->Su, Francis E., et al. "Fibonacci Number Formula." Math Fun Facts.
8-
<http://www.math.hmc.edu/funfacts>
9-
"""
10-
import functools
11-
import math
12-
import time
13-
from decimal import Decimal, getcontext
14-
15-
getcontext().prec = 100
16-
17-
18-
def timer_decorator(func):
19-
@functools.wraps(func)
20-
def timer_wrapper(*args, **kwargs):
21-
start = time.time()
22-
func(*args, **kwargs)
23-
end = time.time()
24-
if int(end - start) > 0:
25-
print(f"Run time for {func.__name__}: {(end - start):0.2f}s")
26-
else:
27-
print(f"Run time for {func.__name__}: {(end - start)*1000:0.2f}ms")
28-
return func(*args, **kwargs)
29-
30-
return timer_wrapper
31-
32-
33-
# define Python user-defined exceptions
34-
class Error(Exception):
35-
"""Base class for other exceptions"""
36-
37-
38-
class ValueTooLargeError(Error):
39-
"""Raised when the input value is too large"""
40-
41-
42-
class ValueTooSmallError(Error):
43-
"""Raised when the input value is not greater than one"""
44-
45-
46-
class ValueLessThanZero(Error):
47-
"""Raised when the input value is less than zero"""
48-
49-
50-
def _check_number_input(n, min_thresh, max_thresh=None):
51-
"""
52-
:param n: single integer
53-
:type n: int
54-
:param min_thresh: min threshold, single integer
55-
:type min_thresh: int
56-
:param max_thresh: max threshold, single integer
57-
:type max_thresh: int
58-
:return: boolean
59-
"""
60-
try:
61-
if n >= min_thresh and max_thresh is None:
62-
return True
63-
elif min_thresh <= n <= max_thresh:
64-
return True
65-
elif n < 0:
66-
raise ValueLessThanZero
67-
elif n < min_thresh:
68-
raise ValueTooSmallError
69-
elif n > max_thresh:
70-
raise ValueTooLargeError
71-
except ValueLessThanZero:
72-
print("Incorrect Input: number must not be less than 0")
73-
except ValueTooSmallError:
74-
print(
75-
f"Incorrect Input: input number must be > {min_thresh} for the recursive "
76-
"calculation"
77-
)
78-
except ValueTooLargeError:
79-
print(
80-
f"Incorrect Input: input number must be < {max_thresh} for the recursive "
81-
"calculation"
82-
)
83-
return False
84-
85-
86-
@timer_decorator
87-
def fib_iterative(n):
88-
"""
89-
:param n: calculate Fibonacci to the nth integer
90-
:type n:int
91-
:return: Fibonacci sequence as a list
92-
"""
93-
n = int(n)
94-
if _check_number_input(n, 2):
95-
seq_out = [0, 1]
96-
a, b = 0, 1
97-
for _ in range(n - len(seq_out)):
98-
a, b = b, a + b
99-
seq_out.append(b)
100-
return seq_out
101-
102-
103-
@timer_decorator
104-
def fib_formula(n):
105-
"""
106-
:param n: calculate Fibonacci to the nth integer
107-
:type n:int
108-
:return: Fibonacci sequence as a list
109-
"""
110-
seq_out = [0, 1]
111-
n = int(n)
112-
if _check_number_input(n, 2, 1000000):
113-
sqrt = Decimal(math.sqrt(5))
114-
phi_1 = Decimal(1 + sqrt) / Decimal(2)
115-
phi_2 = Decimal(1 - sqrt) / Decimal(2)
116-
for i in range(2, n):
117-
temp_out = ((phi_1 ** Decimal(i)) - (phi_2 ** Decimal(i))) * (
118-
Decimal(sqrt) ** Decimal(-1)
119-
)
120-
seq_out.append(int(temp_out))
121-
return seq_out
122-
123-
124-
if __name__ == "__main__":
125-
num = 20
126-
# print(f'{fib_recursive(num)}\n')
127-
# print(f'{fib_iterative(num)}\n')
128-
# print(f'{fib_formula(num)}\n')
129-
fib_iterative(num)
130-
fib_formula(num)
1+
# fibonacci.py
2+
"""
3+
Calculates the Fibonacci sequence using iteration, recursion, and a simplified
4+
form of Binet's formula
5+
6+
NOTE 1: the iterative and recursive functions are more accurate than the Binet's
7+
formula function because the iterative function doesn't use floats
8+
9+
NOTE 2: the Binet's formula function is much more limited in the size of inputs
10+
that it can handle due to the size limitations of Python floats
11+
"""
12+
13+
from math import sqrt
14+
from time import time
15+
16+
17+
def time_func(func, *args, **kwargs):
18+
"""
19+
Times the execution of a function with parameters
20+
"""
21+
start = time()
22+
output = func(*args, **kwargs)
23+
end = time()
24+
if int(end - start) > 0:
25+
print(f"{func.__name__} runtime: {(end - start):0.4f} s")
26+
else:
27+
print(f"{func.__name__} runtime: {(end - start) * 1000:0.4f} ms")
28+
return output
29+
30+
31+
def fib_iterative(n: int) -> list[int]:
32+
"""
33+
Calculates the first n (0-indexed) Fibonacci numbers using iteration
34+
>>> fib_iterative(0)
35+
[0]
36+
>>> fib_iterative(1)
37+
[0, 1]
38+
>>> fib_iterative(5)
39+
[0, 1, 1, 2, 3, 5]
40+
>>> fib_iterative(10)
41+
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
42+
>>> fib_iterative(-1)
43+
Traceback (most recent call last):
44+
...
45+
Exception: n is negative
46+
"""
47+
if n < 0:
48+
raise Exception("n is negative")
49+
if n == 0:
50+
return [0]
51+
fib = [0, 1]
52+
for _ in range(n - 1):
53+
fib.append(fib[-1] + fib[-2])
54+
return fib
55+
56+
57+
def fib_recursive(n: int) -> list[int]:
58+
"""
59+
Calculates the first n (0-indexed) Fibonacci numbers using recursion
60+
>>> fib_iterative(0)
61+
[0]
62+
>>> fib_iterative(1)
63+
[0, 1]
64+
>>> fib_iterative(5)
65+
[0, 1, 1, 2, 3, 5]
66+
>>> fib_iterative(10)
67+
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
68+
>>> fib_iterative(-1)
69+
Traceback (most recent call last):
70+
...
71+
Exception: n is negative
72+
"""
73+
74+
def fib_recursive_term(i: int) -> int:
75+
"""
76+
Calculates the i-th (0-indexed) Fibonacci number using recursion
77+
"""
78+
if i < 0:
79+
raise Exception("n is negative")
80+
if i < 2:
81+
return i
82+
return fib_recursive_term(i - 1) + fib_recursive_term(i - 2)
83+
84+
if n < 0:
85+
raise Exception("n is negative")
86+
return [fib_recursive_term(i) for i in range(n + 1)]
87+
88+
89+
def fib_binet(n: int) -> list[int]:
90+
"""
91+
Calculates the first n (0-indexed) Fibonacci numbers using a simplified form
92+
of Binet's formula:
93+
https://en.m.wikipedia.org/wiki/Fibonacci_number#Computation_by_rounding
94+
95+
NOTE 1: this function diverges from fib_iterative at around n = 71, likely
96+
due to compounding floating-point arithmetic errors
97+
98+
NOTE 2: this function overflows on n >= 1475 because of the size limitations
99+
of Python floats
100+
>>> fib_binet(0)
101+
[0]
102+
>>> fib_binet(1)
103+
[0, 1]
104+
>>> fib_binet(5)
105+
[0, 1, 1, 2, 3, 5]
106+
>>> fib_binet(10)
107+
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
108+
>>> fib_binet(-1)
109+
Traceback (most recent call last):
110+
...
111+
Exception: n is negative
112+
>>> fib_binet(1475)
113+
Traceback (most recent call last):
114+
...
115+
Exception: n is too large
116+
"""
117+
if n < 0:
118+
raise Exception("n is negative")
119+
if n >= 1475:
120+
raise Exception("n is too large")
121+
sqrt_5 = sqrt(5)
122+
phi = (1 + sqrt_5) / 2
123+
return [round(phi ** i / sqrt_5) for i in range(n + 1)]
124+
125+
126+
if __name__ == "__main__":
127+
num = 20
128+
time_func(fib_iterative, num)
129+
time_func(fib_recursive, num)
130+
time_func(fib_binet, num)

maths/fibonacci_sequence_recursion.py

-22
This file was deleted.

0 commit comments

Comments
 (0)