피보나치 시퀀스의 효율적인 파이썬 생성기
이 시퀀스의 가장 짧은 Pythonic 생성을 얻으려고 노력 하면서이 질문을 찾았습니다 (나중에 Python Enhancement Proposal 에서 비슷한 것을 보았습니다 ). 그리고 다른 사람이 내 특정 솔루션을 생각해 낸 것을 보지 못했습니다 (최상의 답변이지만) 가까워 지지만 여전히 우아하지는 않습니다.) 독자가 이해하는 데 도움이 될 수 있다고 생각하기 때문에 첫 번째 반복을 설명하는 주석이 있습니다.
def fib():
a, b = 0, 1
while True: # First iteration:
yield a # yield 0 to start with and then
a, b = b, a + b # a will now be 1, and b will also be 1, (0 + 1)
그리고 사용법 :
for index, fibonacci_number in zip(range(10), fib()):
print('{i:3}: {f:3}'.format(i=index, f=fibonacci_number))
인쇄물:
0: 0
1: 1
2: 1
3: 2
4: 3
5: 5
6: 8
7: 13
8: 21
9: 34
10: 55
(저는 저작자 표시를 위해 최근 에 변수에 대한 파이썬 문서에서 변수 와를 사용하여 유사한 구현 을 발견했습니다 .이 답변을 작성하기 전에 보았던 것을 기억합니다. 그러나이 답변은 언어의 더 나은 사용법을 보여줍니다.)a
b
재귀 적으로 정의 된 구현
정수 시퀀스 의 온라인 백과 사전은 피보나치 시퀀스를 다음과 같이 재귀 적으로 정의합니다.
F (n) = F (n-1) + F (n-2), F (0) = 0 및 F (1) = 1
파이썬에서 이것을 재귀 적으로 간결하게 정의하는 것은 다음과 같이 수행 할 수 있습니다.
def rec_fib(n):
'''inefficient recursive function as defined, returns Fibonacci number'''
if n > 1:
return rec_fib(n-1) + rec_fib(n-2)
return n
그러나 수학 정의의 정확한 표현은 계산되는 각 숫자가 그 아래의 모든 숫자에 대해서도 계산해야하기 때문에 30보다 훨씬 큰 숫자에 대해서는 매우 비효율적입니다. 다음을 사용하여 속도가 얼마나 느린 지 보여줄 수 있습니다.
for i in range(40):
print(i, rec_fib(i))
효율성을위한 메모 된 재귀
속도를 높이기 위해 메모 할 수 있습니다 (이 예제는 함수가 호출 될 때마다 기본 키워드 인수가 동일한 객체라는 사실을 이용하지만 일반적으로 정확하게 이러한 이유로 변경 가능한 기본 인수를 사용하지 않습니다).
def mem_fib(n, _cache={}):
'''efficiently memoized recursive function, returns a Fibonacci number'''
if n in _cache:
return _cache[n]
elif n > 1:
return _cache.setdefault(n, mem_fib(n-1) + mem_fib(n-2))
return n
메모 된 버전이 훨씬 빠르며 커피를 마시기 전에 최대 재귀 깊이를 빠르게 초과합니다. 이렇게하면 시각적으로 얼마나 빠른지 알 수 있습니다.
for i in range(40):
print(i, mem_fib(i))
(아래와 같이 할 수있는 것처럼 보이지만 실제로는 setdefault가 호출되기 전에 자체 호출하기 때문에 캐시를 활용할 수 없습니다.)
def mem_fib(n, _cache={}):
'''don't do this'''
if n > 1:
return _cache.setdefault(n, mem_fib(n-1) + mem_fib(n-2))
return n
재귀 적으로 정의 된 생성기 :
Haskell을 배우면서 Haskell에서이 구현을 보았습니다.
fib@(0:tfib) = 0:1: zipWith (+) fib tfib
내가 지금 파이썬에서 이것에 도달 할 수있는 가장 가까운 것은 다음과 같습니다.
from itertools import tee
def fib():
yield 0
yield 1
# tee required, else with two fib()'s algorithm becomes quadratic
f, tf = tee(fib())
next(tf)
for a, b in zip(f, tf):
yield a + b
이것은 그것을 보여줍니다 :
[f for _, f in zip(range(999), fib())]
그러나 재귀 한계까지만 올라갈 수 있습니다. Haskell 버전은 최대 8 억 개까지 올라갈 수 있지만 노트북의 메모리는 8GB를 모두 사용하지만 일반적으로 1000입니다.
> length $ take 100000000 fib
100000000
n 번째 피보나치 수를 얻기 위해 반복자를 소비
논평자가 묻습니다.
반복자를 기반으로하는 Fib () 함수에 대한 질문 : 예를 들어 10 번째 fib 번호와 같이 n 번째를 얻으려면 어떻게해야합니까?
itertools 문서에는이를위한 레시피가 있습니다.
from itertools import islice
def nth(iterable, n, default=None):
"Returns the nth item or a default value"
return next(islice(iterable, n, None), default)
그리고 지금:
>>> nth(fib(), 10)
55