Je sais qu'il n'y a rien de mal à écrire avec une structure de fonction appropriée, mais j'aimerais savoir comment puis-je trouver un nième nombre fibonacci avec la plupart des méthodes pythoniques avec une ligne.
J'ai écrit ce code, mais cela ne me semblait pas être le meilleur moyen:
>>> fib=lambda n:reduce(lambda x,y:(x[0]+x[1],x[0]),[(1,1)]*(n-2))[0]
>>> fib(8)
13
Comment cela pourrait-il être meilleur et plus simple?
fib = lambda n:reduce(lambda x,n:[x[1],x[0]+x[1]], range(n),[0,1])[0]
(ceci maintient un tuple mappé de [a, b] à [b, a + b], initialisé à [0,1], itéré N fois, puis prend le premier élément Tuple)
>>> fib(1000)
43466557686937456435688527675040625802564660517371780402481729089536555417949051
89040387984007925516929592259308032263477520968962323987332247116164299644090653
3187938298969649928516003704476137795166849228875L
(notez que dans cette numérotation, fib (0) = 0, fib (1) = 1, fib (2) = 1, fib (3) = 2, etc.)
(remarquez également que reduce
est intégré à Python 2.7 mais pas à Python 3; vous devez exécuter from functools import reduce
dans Python 3.)
Un truc rarement vu est qu'une fonction lambda peut se référer à elle-même de manière récursive:
fib = lambda n: n if n < 2 else fib(n-1) + fib(n-2)
À propos, on le voit rarement parce que c'est déroutant et, dans ce cas, c'est aussi inefficace. Mieux vaut l'écrire sur plusieurs lignes:
def fibs():
a = 0
b = 1
while True:
yield a
a, b = b, a + b
J'ai récemment appris à utiliser la multiplication matricielle pour générer des nombres de Fibonacci, ce qui était plutôt cool. Vous prenez une matrice de base:
[1, 1]
[1, 0]
et multipliez-le par lui-même N fois pour obtenir:
[F(N+1), F(N)]
[F(N), F(N-1)]
Ce matin, gribouillant à la vapeur sur le mur de la douche, je me suis rendu compte que vous pouviez réduire le temps de fonctionnement de moitié en commençant par la deuxième matrice et en le multipliant par lui-même N/2 fois, puis en utilisant N pour choisir un index dans le premier. rangée/colonne.
Avec un peu de pression, je l'ai réduit à une ligne:
import numpy
def mm_fib(n):
return (numpy.matrix([[2,1],[1,1]])**(n//2))[0,(n+1)%2]
>>> [mm_fib(i) for i in range(20)]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181]
Si nous considérons que la "manière la plus pythonique" est élégante et efficace, alors:
def fib(nr):
return int(((1 + math.sqrt(5)) / 2) ** nr / math.sqrt(5) + 0.5)
gagne haut la main. Pourquoi utiliser un algorithme inefficace (et si vous commencez à utiliser la mémoisation, nous pouvons oublier le oneliner) alors que vous pouvez résoudre le problème correctement en O(1) en approximant le résultat avec le nombre d'or? Bien qu'en réalité, je l'écrirais évidemment sous cette forme:
def fib(nr):
ratio = (1 + math.sqrt(5)) / 2
return int(ratio ** nr / math.sqrt(5) + 0.5)
Plus efficace et beaucoup plus facile à comprendre.
Ceci est une ligne non mémorisable (anonyme)
fib = lambda x,y=[1,1]:([(y.append(y[-1]+y[-2]),y[-1])[1] for i in range(1+x-len(y))],y[x])[1]
fib = lambda n, x=0, y=1 : x if not n else fib(n-1, y, x+y)
temps d'exécution O (n), fib (0) = 0, fib (1) = 1, fib (2) = 1 ...
Autre exemple, inspiré de la réponse de Mark Byers:
fib = lambda n,a=0,b=1: a if n<=0 else fib(n-1,b,a+b)
C'est une expression fermée pour la série de Fibonacci qui utilise l'arithmétique entière et qui est assez efficace.
fib = lambda n:pow(2<<n,n+1,(4<<2*n)-(2<<n)-1)%(2<<n)
>> fib(1000)
4346655768693745643568852767504062580256466051737178
0402481729089536555417949051890403879840079255169295
9225930803226347752096896232398733224711616429964409
06533187938298969649928516003704476137795166849228875L
Il calcule le résultat en O (log n) opérations arithmétiques, chacune agissant sur des entiers avec O(n) bits. Étant donné que le résultat (le nième nombre de Fibonacci) est O(n) bits, la méthode est tout à fait raisonnable.
Il est basé sur genefib4
from http://fare.tunes.org/files/fun/fibonacci.LISP , qui est lui-même basé sur une expression entière moins efficace de forme fermée (voir: http : //paulhankin.github.io/Fibonacci/ )
Voici une implémentation qui n'utilise pas la récursivité et ne mémorise que les deux dernières valeurs au lieu de l'historique de la séquence.
nthfib () ci-dessous est la solution directe au problème initial (tant que les importations sont autorisées)
C’est moins élégant que d’utiliser les méthodes de réduction ci-dessus, mais, bien que légèrement différent de ce qui a été demandé, il a la possibilité d’être utilisé plus efficacement comme générateur infini s’il faut également sortir la séquence jusqu’au n ré-écriture légèrement comme fibgen () ci-dessous).
from itertools import imap, islice, repeat
nthfib = lambda n: next(islice((lambda x=[0, 1]: imap((lambda x: (lambda setx=x.__setitem__, x0_temp=x[0]: (x[1], setx(0, x[1]), setx(1, x0_temp+x[1]))[0])()), repeat(x)))(), n-1, None))
>>> nthfib(1000)
43466557686937456435688527675040625802564660517371780402481729089536555417949051
89040387984007925516929592259308032263477520968962323987332247116164299644090653
3187938298969649928516003704476137795166849228875L
from itertools import imap, islice, repeat
fibgen = lambda:(lambda x=[0,1]: imap((lambda x: (lambda setx=x.__setitem__, x0_temp=x[0]: (x[1], setx(0, x[1]), setx(1, x0_temp+x[1]))[0])()), repeat(x)))()
>>> list(islice(fibgen(),12))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144]
Mes 2 centimes
# One Liner
def nthfibonacci(n):
return long(((((1+5**.5)/2)**n)-(((1-5**.5)/2)**n))/5**.5)
OU
# Steps
def nthfibonacci(nth):
sq5 = 5**.5
phi1 = (1+sq5)/2
phi2 = -1 * (phi1 -1)
n1 = phi1**(nth+1)
n2 = phi2**(nth+1)
return long((n1 - n2)/sq5)
Pour résoudre ce problème, je me suis inspiré d'une question similaire dans Stackoverflow Single Statement Fibonacci , et cette fonction à une seule ligne permet de générer une liste de séquences de fibonacci. Bien que ceci soit un script Python 2, non testé sur Python 3:
(lambda n, fib=[0,1]: fib[:n]+[fib.append(fib[-1] + fib[-2]) or fib[-1] for i in range(n-len(fib))])(10)
assigne cette fonction lambda à une variable pour la réutiliser:
fib = (lambda n, fib=[0,1]: fib[:n]+[fib.append(fib[-1] + fib[-2]) or fib[-1] for i in range(n-len(fib))])
fib(10)
la sortie est une liste de la séquence de fibonacci:
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
def fib(n):
x =[0,1]
for i in range(n):
x=[x[1],x[0]+x[1]]
return x[0]
comme Jason S, je pense que ma version a une meilleure compréhension.
Je ne sais pas si c'est la méthode la plus pythonique mais c'est la meilleure que je puisse trouver: ->
Fibonacci = lambda x,y=[1,1]:[1]*x if (x<2) else ([y.append(y[q-1] + y[q-2]) for q in range(2,x)],y)[1]
Le code ci-dessus n'utilise pas de récursivité, mais seulement une liste pour stocker les valeurs.
Je suis un nouveau venu dans Python, mais j'ai pris certaines mesures à des fins d'apprentissage. J'ai collecté un algorithme fibo et pris des mesures.
from datetime import datetime
import matplotlib.pyplot as plt
from functools import wraps
from functools import reduce
from functools import lru_cache
import numpy
def time_it(f):
@wraps(f)
def wrapper(*args, **kwargs):
start_time = datetime.now()
f(*args, **kwargs)
end_time = datetime.now()
elapsed = end_time - start_time
elapsed = elapsed.microseconds
return elapsed
return wrapper
@time_it
def fibslow(n):
if n <= 1:
return n
else:
return fibslow(n-1) + fibslow(n-2)
@time_it
@lru_cache(maxsize=10)
def fibslow_2(n):
if n <= 1:
return n
else:
return fibslow_2(n-1) + fibslow_2(n-2)
@time_it
def fibfast(n):
if n <= 1:
return n
a, b = 0, 1
for i in range(1, n+1):
a, b = b, a + b
return a
@time_it
def fib_reduce(n):
return reduce(lambda x, n: [x[1], x[0]+x[1]], range(n), [0, 1])[0]
@time_it
def mm_fib(n):
return (numpy.matrix([[2, 1], [1, 1]])**(n//2))[0, (n+1) % 2]
@time_it
def fib_ia(n):
return pow(2 << n, n+1, (4 << 2 * n) - (2 << n)-1) % (2 << n)
if __== '__main__':
X = range(1, 200)
# fibslow_times = [fibslow(i) for i in X]
fibslow_2_times = [fibslow_2(i) for i in X]
fibfast_times = [fibfast(i) for i in X]
fib_reduce_times = [fib_reduce(i) for i in X]
fib_mm_times = [mm_fib(i) for i in X]
fib_ia_times = [fib_ia(i) for i in X]
# print(fibslow_times)
# print(fibfast_times)
# print(fib_reduce_times)
plt.figure()
# plt.plot(X, fibslow_times, label='Slow Fib')
plt.plot(X, fibslow_2_times, label='Slow Fib w cache')
plt.plot(X, fibfast_times, label='Fast Fib')
plt.plot(X, fib_reduce_times, label='Reduce Fib')
plt.plot(X, fib_mm_times, label='Numpy Fib')
plt.plot(X, fib_ia_times, label='Fib ia')
plt.xlabel('n')
plt.ylabel('time (microseconds)')
plt.legend()
plt.show()
Le résultat est généralement le même .
Fiboslow_2 avec récursivité et cache, l'arithmétique des entiers Fib et les algorithmes Fibfast semblent les meilleurs. Peut-être que mon décorateur n’est pas la meilleure chose à faire pour mesurer la performance, mais pour un aperçu général, cela semblait bien.
Pourquoi ne pas utiliser une liste de compréhension?
from math import sqrt, floor
[floor(((1+sqrt(5))**n-(1-sqrt(5))**n)/(2**n*sqrt(5))) for n in range(100)]
Sans importations mathématiques, mais moins jolies:
[int(((1+(5**0.5))**n-(1-(5**0.5))**n)/(2**n*(5**0.5))) for n in range(100)]
Vous pouvez générer une fois une liste avec certaines valeurs et utiliser au besoin:
fib = lambda x: 1 if x <=2 else fib_fix[x-3] if x-2 <= len(fib_fix) else (fib_fix.append(fib(x-2) + fib(x-1)) or fib_fix[-1])
fib_x = lambda x: [fib(n) for n in range(1,x+1)]
fib_100 = fib_x(100)
que par exemple:
a = fib_fix[76]
voici comment je le fais, cependant la fonction retourne None pour la partie de la ligne de compréhension de liste pour me permettre d'insérer une boucle à l'intérieur .. sur deux éléments
>>f=lambda list,x :print('The list must be of 2 or more') if len(list)<2 else [list.append(list[-1]+list[-2]) for i in range(x)]
>>a=[1,2]
>>f(a,7)
Lambda avec opérateurs logiques
fibonacci_oneline = lambda n = 10, out = []: [ out.append(i) or i if i <= 1 else out.append(out[-1] + out[-2]) or out[-1] for i in range(n)]
Similaire:
def fibonacci(n):
f=[1]+[0]
for i in range(n):
f=[sum(f)] + f[:-1]
print f[1]
Un simple générateur de nombre de Fibonacci utilisant la récursivité
fib = lambda x: 1-x if x < 2 else fib(x-1)+fib(x-2)
print fib(100)
Cela prend une éternité pour calculer fib(100)
dans mon ordinateur.
Il existe également forme fermée des nombres de Fibonacci.
fib = lambda n: int(1/sqrt(5)*((1+sqrt(5))**n-(1-sqrt(5))**n)/2**n)
print fib(50)
Cela fonctionne presque jusqu'à 72 numéros en raison d'un problème de précision.