Nombres de Fibonacci, avec un one-liner en Python 3?

Je sais qu'il n'y a rien de mal à écrire avec une structure de fonction appropriée, mais je voudrais savoir comment puis-je trouver le nième nombre de fibonacci avec la plupart des moyens Pythoniques avec une seule ligne.

J'ai écrit ce code, mais cela ne me semblait pas 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 pourrait-il être meilleur et plus simple?

36
demandé sur utdemir 2011-02-08 20:01:34

19 réponses

fib = lambda n:reduce(lambda x,n:[x[1],x[0]+x[1]], range(n),[0,1])[0]

(cela 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

(à noter que dans cette numérotation, fib(0) = 0, fib(1) = 1, fib(2) = 1, fib(3) = 2, etc.)

42
répondu Jason S 2011-02-08 17:15:39

Un truc rarement vu est qu'une fonction lambda peut se référer à elle-même récursivement:

fib = lambda n: n if n < 2 else fib(n-1) + fib(n-2)

En passant, c'est rarement vu parce que c'est confus, et dans ce cas c'est aussi inefficace. Il est préférable de l'écrire sur plusieurs lignes:

def fibs():
    a = 0
    b = 1
    while True:
        yield a
        a, b = b, a + b
33
répondu Mark Byers 2011-02-08 17:11:08

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, en griffonnant dans la vapeur sur le mur de la douche, j'ai réalisé que vous pouviez réduire le temps de fonctionnement en deux en commençant par la deuxième matrice, et en la multipliant par elle-même n / 2 fois, puis en utilisant N pour choisir un index de la première rangée / colonne.

Avec un peu de compression, Je Je l'ai ramené à 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]
10
répondu Chris Doggett 2014-09-24 18:40:58

Si nous considérons la "manière la plus pythonique" comme é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émorisation, nous pouvons oublier le oneliner) lorsque vous pouvez résoudre le problème très bien dans 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.

9
répondu Voo 2011-02-08 17:14:11

Il s'agit d'une mémorisation non récursive (anonyme) d'un liner

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]
8
répondu 6502 2012-11-02 20:26:51
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 ...

4
répondu dalef 2011-04-18 00:25:52

Un autre exemple, en prenant le signal 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
répondu Jason S 2011-02-08 17:30:42

Ceci est une expression fermée pour la série de Fibonacci qui utilise l'arithmétique entière, et 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 opérations arithmétiques O(log n), chacune agissant sur des entiers avec des bits O(N). É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 de http://fare.tunes.org/files/fun/fibonacci.lisp , qui à son tour était basé sur une expression entière de forme fermée moins efficace de moi (voir: http://paulhankin.github.io/Fibonacci/)

3
répondu Paul Hankin 2016-05-29 11:33:54

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 complet de la séquence.

Nthfib() ci-dessous est la solution directe au problème d'origine (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 gagne la capacité d'être utilisé plus efficacement comme un générateur infini si l'on a besoin de sortir la séquence jusqu'à la nième nombre ainsi (réécrire 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]
1
répondu Jon Schoning 2011-06-10 22:23:39

Pour résoudre ce problème, je me suis inspiré d'une question similaire ici dans Stackoverflow single Statement Fibonacci , et j'ai eu cette fonction de ligne unique qui peut produire une liste de séquence de fibonacci. Cependant, c'est 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)

Affectez 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 séquence de fibonacci:

[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
1
répondu MMSs 2017-05-23 12:02:51
def fib(n):
    x =[0,1]
    for i in range(n):
        x=[x[1],x[0]+x[1]]
    return x[0]

Prenez le signal de Jason S, je pense que ma version a une meilleure compréhension.

1
répondu bigtan 2014-01-14 10:40:20

Je ne sais pas si c'est la méthode la plus pythonique mais c'est le meilleur 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 la récursivité, juste une liste pour stocker les valeurs.

1
répondu Edwin Clement 2014-08-02 17:59:38

Mes 2 cents

# 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
répondu Adeel 2014-09-25 10:26:13

Pourquoi ne pas utiliser une compréhension de liste?

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 jolie:

[int(((1+(5**0.5))**n-(1-(5**0.5))**n)/(2**n*(5**0.5))) for n in range(100)]
1
répondu Sanjurjo7 2017-01-18 15:19:16

Je suis un nouveau venu Python, mais j'ai fait une certaine mesure à des fins d'apprentissage. J'ai recueilli un algorithme fibo et pris une certaine mesure.

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 __name__ == '__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. entrez la description de l'image ici

Fiboslow_2 avec récursion et cache, l'arithmétique des entiers Fib et les algorithmes Fibfast semblent les meilleurs. Peut-être mon décorateur pas la meilleure chose à mesurer la performance, mais pour un aperçu, il semblait bon.

1
répondu tkircsi 2018-09-17 20:51:10

Similaire:

    def fibonacci(n):
      f=[1]+[0]
      for i in range(n):
        f=[sum(f)] + f[:-1]
      print f[1]
0
répondu Stefan Gruenwald 2014-02-21 18:05:21

Un simple générateur de nombres 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 y a aussi 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
répondu Santosh Linkha 2015-06-08 11:14:00

Lambda avec des 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
répondu sriharsha_bhat 2016-03-09 07:10:27

Voici comment je le fais, mais la fonction renvoie None pour la partie Ligne de compréhension de liste pour me permettre d'insérer une boucle à l'intérieur .. donc, fondamentalement, ce qu'il fait est d'ajouter de nouveaux éléments de la FIB seq à l'intérieur d'une liste qui est 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
répondu Ahmed Elbarky 2016-05-29 09:54:20