Fibonacci dizisinin verimli Pythonic jeneratörü
Bu dizinin en kısa Pythonic neslini elde etmeye çalışırken bu soruyu buldum (daha sonra bir Python Geliştirme Teklifinde benzer bir tane gördüğümü fark ettim) ve özel çözümümle başka kimsenin geldiğini fark etmedim (en iyi cevap yaklaşıyor, ancak yine de daha az zarif), işte burada, ilk yinelemeyi açıklayan yorumlar ile, çünkü okuyucuların anlamasına yardımcı olabilir:
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)
ve kullanım:
for index, fibonacci_number in zip(range(10), fib()):
print('{i:3}: {f:3}'.format(i=index, f=fibonacci_number))
baskılar:
0: 0
1: 1
2: 1
3: 2
4: 3
5: 5
6: 8
7: 13
8: 21
9: 34
10: 55
(Atıf amacıyla, Geçenlerde bir fark benzer uygulama bile değişkenleri kullanarak, modülleri Python belgelerinde a
ve b
şimdi bu cevabı yazmadan önce gördükten hatırlamak. Ama bu yanıt dilin daha iyi kullanımını gösteren düşünüyorum.)
Yinelemeli olarak tanımlanmış uygulama
Tamsayı Dizilerin Çevrimiçi Ansiklopedisi olarak yinelemeli Fibonacci Dizisi tanımlar
F (n) = F (n-1) + F (n-2) ile F (0) = 0 ve F (1) = 1
Bunu Python'da özyineli olarak tanımlayan aşağıdaki gibi yapılabilir:
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
Ancak, matematiksel tanımın bu tam gösterimi 30'dan büyük sayılar için inanılmaz derecede verimsizdir, çünkü hesaplanan her sayı, altındaki her sayı için de hesaplanmalıdır. Aşağıdakileri kullanarak ne kadar yavaş olduğunu gösterebilirsiniz:
for i in range(40):
print(i, rec_fib(i))
Verimlilik için not edilmiş özyineleme
Hızı artırmak için not edilebilir (bu örnek, işlev her çağrıldığında varsayılan bir anahtar kelime bağımsız değişkeninin aynı nesne olduğu gerçeğinden yararlanır, ancak normalde tam olarak bu nedenle değiştirilebilir bir varsayılan bağımsız değişken kullanmazsınız):
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
Memoized sürümünün çok daha hızlı olduğunu ve kahve almaya kalkmadan önce maksimum özyineleme derinliğinizi hızla aşacağını göreceksiniz. Bunu yaparak görsel olarak ne kadar hızlı olduğunu görebilirsiniz:
for i in range(40):
print(i, mem_fib(i))
(Aşağıdakileri yapabileceğimiz gibi görünebilir, ancak aslında önbellekten faydalanmamıza izin vermez, çünkü setdefault çağrılmadan önce kendini çağırır.)
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
Özyinelemeli tanımlı jeneratör:
Haskell öğrenirken, Haskell'de bu uygulama ile karşılaştım:
fib@(0:tfib) = 0:1: zipWith (+) fib tfib
Ben şu anda Python bu alabilirsiniz düşünüyorum en yakın:
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
Bu bunu gösterir:
[f for _, f in zip(range(999), fib())]
Yine de, özyineleme sınırına kadar çıkabilir. Genellikle 1000, Haskell sürümü 100 milyona kadar çıkabiliyor, ancak bunu yapmak için dizüstü bilgisayarımın hafızasının tamamını kullanıyor olsa da:
> length $ take 100000000 fib
100000000
Nci fibonacci numarasını almak için iteratörü kullanma
Bir yorumcu soruyor:
Yineleyiciye dayalı Fib () işlevi için soru: n., Örneğin 10. fib numarasını almak istiyorsanız ne olacak?
İtertools belgelerinin bunun için bir tarifi vardır:
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)
ve şimdi:
>>> nth(fib(), 10)
55