add a yield method to fibonaci (#10826)

* add a yiled method to fibonaci

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fibonaci

* Update fibonacci.py

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update fibonacci.py

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Christian Clauss <cclauss@me.com>
This commit is contained in:
dragon 2023-10-29 23:40:01 +08:00 committed by GitHub
parent d59cf1734f
commit 3ad90cea83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,4 +1,3 @@
# fibonacci.py
""" """
Calculates the Fibonacci sequence using iteration, recursion, memoization, Calculates the Fibonacci sequence using iteration, recursion, memoization,
and a simplified form of Binet's formula and a simplified form of Binet's formula
@ -9,14 +8,12 @@ the Binet's formula function because the Binet formula function uses floats
NOTE 2: the Binet's formula function is much more limited in the size of inputs 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 that it can handle due to the size limitations of Python floats
RESULTS: (n = 20) See benchmark numbers in __main__ for performance comparisons/
fib_iterative runtime: 0.0055 ms https://en.wikipedia.org/wiki/Fibonacci_number for more information
fib_recursive runtime: 6.5627 ms
fib_memoization runtime: 0.0107 ms
fib_binet runtime: 0.0174 ms
""" """
import functools import functools
from collections.abc import Iterator
from math import sqrt from math import sqrt
from time import time from time import time
@ -35,6 +32,31 @@ def time_func(func, *args, **kwargs):
return output return output
def fib_iterative_yield(n: int) -> Iterator[int]:
"""
Calculates the first n (1-indexed) Fibonacci numbers using iteration with yield
>>> list(fib_iterative_yield(0))
[0]
>>> tuple(fib_iterative_yield(1))
(0, 1)
>>> tuple(fib_iterative_yield(5))
(0, 1, 1, 2, 3, 5)
>>> tuple(fib_iterative_yield(10))
(0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55)
>>> tuple(fib_iterative_yield(-1))
Traceback (most recent call last):
...
ValueError: n is negative
"""
if n < 0:
raise ValueError("n is negative")
a, b = 0, 1
yield a
for _ in range(n):
yield b
a, b = b, a + b
def fib_iterative(n: int) -> list[int]: def fib_iterative(n: int) -> list[int]:
""" """
Calculates the first n (0-indexed) Fibonacci numbers using iteration Calculates the first n (0-indexed) Fibonacci numbers using iteration
@ -49,10 +71,10 @@ def fib_iterative(n: int) -> list[int]:
>>> fib_iterative(-1) >>> fib_iterative(-1)
Traceback (most recent call last): Traceback (most recent call last):
... ...
Exception: n is negative ValueError: n is negative
""" """
if n < 0: if n < 0:
raise Exception("n is negative") raise ValueError("n is negative")
if n == 0: if n == 0:
return [0] return [0]
fib = [0, 1] fib = [0, 1]
@ -75,7 +97,7 @@ def fib_recursive(n: int) -> list[int]:
>>> fib_iterative(-1) >>> fib_iterative(-1)
Traceback (most recent call last): Traceback (most recent call last):
... ...
Exception: n is negative ValueError: n is negative
""" """
def fib_recursive_term(i: int) -> int: def fib_recursive_term(i: int) -> int:
@ -95,13 +117,13 @@ def fib_recursive(n: int) -> list[int]:
Exception: n is negative Exception: n is negative
""" """
if i < 0: if i < 0:
raise Exception("n is negative") raise ValueError("n is negative")
if i < 2: if i < 2:
return i return i
return fib_recursive_term(i - 1) + fib_recursive_term(i - 2) return fib_recursive_term(i - 1) + fib_recursive_term(i - 2)
if n < 0: if n < 0:
raise Exception("n is negative") raise ValueError("n is negative")
return [fib_recursive_term(i) for i in range(n + 1)] return [fib_recursive_term(i) for i in range(n + 1)]
@ -119,7 +141,7 @@ def fib_recursive_cached(n: int) -> list[int]:
>>> fib_iterative(-1) >>> fib_iterative(-1)
Traceback (most recent call last): Traceback (most recent call last):
... ...
Exception: n is negative ValueError: n is negative
""" """
@functools.cache @functools.cache
@ -128,13 +150,13 @@ def fib_recursive_cached(n: int) -> list[int]:
Calculates the i-th (0-indexed) Fibonacci number using recursion Calculates the i-th (0-indexed) Fibonacci number using recursion
""" """
if i < 0: if i < 0:
raise Exception("n is negative") raise ValueError("n is negative")
if i < 2: if i < 2:
return i return i
return fib_recursive_term(i - 1) + fib_recursive_term(i - 2) return fib_recursive_term(i - 1) + fib_recursive_term(i - 2)
if n < 0: if n < 0:
raise Exception("n is negative") raise ValueError("n is negative")
return [fib_recursive_term(i) for i in range(n + 1)] return [fib_recursive_term(i) for i in range(n + 1)]
@ -152,10 +174,10 @@ def fib_memoization(n: int) -> list[int]:
>>> fib_iterative(-1) >>> fib_iterative(-1)
Traceback (most recent call last): Traceback (most recent call last):
... ...
Exception: n is negative ValueError: n is negative
""" """
if n < 0: if n < 0:
raise Exception("n is negative") raise ValueError("n is negative")
# Cache must be outside recursuive function # Cache must be outside recursuive function
# other it will reset every time it calls itself. # other it will reset every time it calls itself.
cache: dict[int, int] = {0: 0, 1: 1, 2: 1} # Prefilled cache cache: dict[int, int] = {0: 0, 1: 1, 2: 1} # Prefilled cache
@ -193,29 +215,30 @@ def fib_binet(n: int) -> list[int]:
>>> fib_binet(-1) >>> fib_binet(-1)
Traceback (most recent call last): Traceback (most recent call last):
... ...
Exception: n is negative ValueError: n is negative
>>> fib_binet(1475) >>> fib_binet(1475)
Traceback (most recent call last): Traceback (most recent call last):
... ...
Exception: n is too large ValueError: n is too large
""" """
if n < 0: if n < 0:
raise Exception("n is negative") raise ValueError("n is negative")
if n >= 1475: if n >= 1475:
raise Exception("n is too large") raise ValueError("n is too large")
sqrt_5 = sqrt(5) sqrt_5 = sqrt(5)
phi = (1 + sqrt_5) / 2 phi = (1 + sqrt_5) / 2
return [round(phi**i / sqrt_5) for i in range(n + 1)] return [round(phi**i / sqrt_5) for i in range(n + 1)]
if __name__ == "__main__": if __name__ == "__main__":
import doctest from doctest import testmod
doctest.testmod()
testmod()
# Time on an M1 MacBook Pro -- Fastest to slowest
num = 30 num = 30
time_func(fib_iterative, num) time_func(fib_iterative_yield, num) # 0.0012 ms
time_func(fib_recursive, num) # Around 3s runtime time_func(fib_iterative, num) # 0.0031 ms
time_func(fib_recursive_cached, num) # Around 0ms runtime time_func(fib_binet, num) # 0.0062 ms
time_func(fib_memoization, num) time_func(fib_memoization, num) # 0.0100 ms
time_func(fib_binet, num) time_func(fib_recursive_cached, num) # 0.0153 ms
time_func(fib_recursive, num) # 257.0910 ms