web-dev-qa-db-fra.com

Numéros de Fibonacci, avec une ligne dans Python 3?

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?

38
utdemir
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.)

41
Jason S

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
34
Mark Byers

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]
12
Chris Doggett

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.

10
Voo

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]
9
6502
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 ...

6
dalef

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)
3
Jason S

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/ )

3
Paul Hankin

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]
2
Jon Schoning

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)
1
Adeel

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]
1
MMSs
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.

1
bigtan

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.

1
Edwin Clement

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 .  enter image description here

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.

1
tkircsi

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)]
1
Sanjurjo7

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]
0
Il Ilya

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)
0
Ahmed Elbarky

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)]
0
sriharsha_bhat

Similaire:

    def fibonacci(n):
      f=[1]+[0]
      for i in range(n):
        f=[sum(f)] + f[:-1]
      print f[1]
0
Stefan Gruenwald

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.

0
Santosh Linkha