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,
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
that it can handle due to the size limitations of Python floats
RESULTS: (n = 20)
fib_iterative runtime: 0.0055 ms
fib_recursive runtime: 6.5627 ms
fib_memoization runtime: 0.0107 ms
fib_binet runtime: 0.0174 ms
See benchmark numbers in __main__ for performance comparisons/
https://en.wikipedia.org/wiki/Fibonacci_number for more information
"""
import functools
from collections.abc import Iterator
from math import sqrt
from time import time
@ -35,6 +32,31 @@ def time_func(func, *args, **kwargs):
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]:
"""
Calculates the first n (0-indexed) Fibonacci numbers using iteration
@ -49,10 +71,10 @@ def fib_iterative(n: int) -> list[int]:
>>> fib_iterative(-1)
Traceback (most recent call last):
...
Exception: n is negative
ValueError: n is negative
"""
if n < 0:
raise Exception("n is negative")
raise ValueError("n is negative")
if n == 0:
return [0]
fib = [0, 1]
@ -75,7 +97,7 @@ def fib_recursive(n: int) -> list[int]:
>>> fib_iterative(-1)
Traceback (most recent call last):
...
Exception: n is negative
ValueError: n is negative
"""
def fib_recursive_term(i: int) -> int:
@ -95,13 +117,13 @@ def fib_recursive(n: int) -> list[int]:
Exception: n is negative
"""
if i < 0:
raise Exception("n is negative")
raise ValueError("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")
raise ValueError("n is negative")
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)
Traceback (most recent call last):
...
Exception: n is negative
ValueError: n is negative
"""
@functools.cache
@ -128,13 +150,13 @@ def fib_recursive_cached(n: int) -> list[int]:
Calculates the i-th (0-indexed) Fibonacci number using recursion
"""
if i < 0:
raise Exception("n is negative")
raise ValueError("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")
raise ValueError("n is negative")
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)
Traceback (most recent call last):
...
Exception: n is negative
ValueError: n is negative
"""
if n < 0:
raise Exception("n is negative")
raise ValueError("n is negative")
# Cache must be outside recursuive function
# other it will reset every time it calls itself.
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)
Traceback (most recent call last):
...
Exception: n is negative
ValueError: n is negative
>>> fib_binet(1475)
Traceback (most recent call last):
...
Exception: n is too large
ValueError: n is too large
"""
if n < 0:
raise Exception("n is negative")
raise ValueError("n is negative")
if n >= 1475:
raise Exception("n is too large")
raise ValueError("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__":
import doctest
doctest.testmod()
from doctest import testmod
testmod()
# Time on an M1 MacBook Pro -- Fastest to slowest
num = 30
time_func(fib_iterative, num)
time_func(fib_recursive, num) # Around 3s runtime
time_func(fib_recursive_cached, num) # Around 0ms runtime
time_func(fib_memoization, num)
time_func(fib_binet, num)
time_func(fib_iterative_yield, num) # 0.0012 ms
time_func(fib_iterative, num) # 0.0031 ms
time_func(fib_binet, num) # 0.0062 ms
time_func(fib_memoization, num) # 0.0100 ms
time_func(fib_recursive_cached, num) # 0.0153 ms
time_func(fib_recursive, num) # 257.0910 ms