Fibonacci numbers, with an one-liner in Python 3?
Asked Answered
F

24

49

I know there is nothing wrong with writing with proper function structure, but I would like to know how can I find nth fibonacci number with most Pythonic way with a one-line.

I wrote that code, but It didn't seem to me best way:

>>> fib = lambda n:reduce(lambda x, y: (x[0]+x[1], x[0]), [(1,1)]*(n-2))[0]
>>> fib(8)
13

How could it be better and simplier?

Female answered 8/2, 2011 at 17:1 Comment(1)
"How could it be better and simplier?" By not being a one-liner.Trejo
A
65
fib = lambda n:reduce(lambda x,n:[x[1],x[0]+x[1]], range(n),[0,1])[0]

(this maintains a tuple mapped from [a,b] to [b,a+b], initialized to [0,1], iterated N times, then takes the first tuple element)

>>> fib(1000)
43466557686937456435688527675040625802564660517371780402481729089536555417949051
89040387984007925516929592259308032263477520968962323987332247116164299644090653
3187938298969649928516003704476137795166849228875L

(note that in this numbering, fib(0) = 0, fib(1) = 1, fib(2) = 1, fib(3) = 2, etc.)

(also note: reduce is a builtin in Python 2.7 but not in Python 3; you'd need to execute from functools import reduce in Python 3.)

Allspice answered 8/2, 2011 at 17:15 Comment(2)
But didn't really understand the solution, x is an integer from [0,1]+range(n), right(I think my mistake is at here)? But we use x[1],x[0]. How? I can't see how we maintain a tuple.Female
reduce's input function takes two arguments, an accumulator and an input: reduce calls the function for each element in the iterable (which is range(n) in this case.) The accumulator in this case is x, which is a tuple, initialized at [0,1]. The function in reduce() outputs a new tuple [x[1],x[0]+x[1]].Allspice
R
53

A rarely seen trick is that a lambda function can refer to itself recursively:

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

By the way, it's rarely seen because it's confusing, and in this case it is also inefficient. It's much better to write it on multiple lines:

def fibs():
    a = 0
    b = 1
    while True:
        yield a
        a, b = b, a + b
Richards answered 8/2, 2011 at 17:5 Comment(7)
1->n, 2->1 allows for fib(0) = 0.Guesthouse
@Jason S, @DSM: Thanks for the comments. Updated answer.Richards
+1 for the generator, I've always found it the most elegant and efficient way to represent the Fibonacci sequence in Python.Folsom
+1 for something that works on fib(0),fib(1),fib(2) unlike OPMythopoeia
I didn't know that lambda can call itself, but it would be good we can refer lambda with a keyword, without assigning it to stg, like we access the class variables with self.Female
@utdmr: self is nothing magical, not even it being passed to methods implicitly needs magic. And no, I don't think this possibility justifies a new keyword.Until
apply memoize pattern: fib = lambda n,m={}: m.get(n) or m.setdefault(n, n if n < 2 else fib(n - 1) + fib(n - 2))Mackie
I
14

I recently learned about using matrix multiplication to generate Fibonacci numbers, which was pretty cool. You take a base matrix:

[1, 1]
[1, 0]

and multiply it by itself N times to get:

[F(N+1), F(N)]
[F(N), F(N-1)]

This morning, doodling in the steam on the shower wall, I realized that you could cut the running time in half by starting with the second matrix, and multiplying it by itself N/2 times, then using N to pick an index from the first row/column.

With a little squeezing, I got it down to one line:

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]
Invoice answered 13/3, 2013 at 18:5 Comment(1)
@winmwx: Numpy arrays support 2D indexing ([i,j]), so with a normal list, it would be like a[0][(1,0)[n%2]]. It's basically getting the top row of the matrix ([F(N+1), F(N)]), then using (1,0)[n%2] to choose which of those two it picks, based on whether N is even or odd. So if N is even, it takes the second one (F(N)), and if it's odd, it takes the first (F(N+1)). Now that I look at it again, I could've just had it use [0, (n+1)%2]. It's off by one since we start with the second matrix ([[2,1],[1,1]]).Invoice
C
13

This is a closed expression for the Fibonacci series that uses integer arithmetic, and is quite efficient.

fib = lambda n:pow(2<<n,n+1,(4<<2*n)-(2<<n)-1)%(2<<n)

>> fib(1000)
4346655768693745643568852767504062580256466051737178
0402481729089536555417949051890403879840079255169295
9225930803226347752096896232398733224711616429964409
06533187938298969649928516003704476137795166849228875L

It computes the result in O(log n) arithmetic operations, each acting on integers with O(n) bits. Given that the result (the nth Fibonacci number) is O(n) bits, the method is quite reasonable.

It's based on genefib4 from http://fare.tunes.org/files/fun/fibonacci.lisp , which in turn was based on an a less efficient closed-form integer expression of mine (see: http://paulhankin.github.io/Fibonacci/)

Chalcis answered 29/5, 2016 at 11:22 Comment(0)
K
11

If we consider the "most Pythonic way" to be elegant and effective then:

def fib(nr):
    return int(((1 + math.sqrt(5)) / 2) ** nr / math.sqrt(5) + 0.5)

wins hands down. Why use a inefficient algorithm (and if you start using memoization we can forget about the oneliner) when you can solve the problem just fine in O(1) by approximation the result with the golden ratio? Though in reality I'd obviously write it in this form:

def fib(nr):
    ratio = (1 + math.sqrt(5)) / 2
    return int(ratio ** nr / math.sqrt(5) + 0.5)

More efficient and much easier to understand.

Keniakenilworth answered 8/2, 2011 at 17:14 Comment(6)
I thought about the explicit Fibonacci formula too, but it has precision problems for large n.Allspice
It has precision problems for small n; fib(71) is wrong. If we're only required to be correct for the first few terms, then def fib(n): return [0, 1, 1, 2, 3, ..][n] is even simpler.. [Updated to address change from round to int in code.]Guesthouse
thanks,actually my main purpose is exploring Python's abilities, not fast calculation :). +1Female
Ok that was short sighted - tested it only for the first 60 values and assumed if it worked there we wouldn't run into precision problems for larger values. Well so much for that. And yeah changed the formula because I thought it should work fine without the explicit rounding.Keniakenilworth
Why do you think memoization rules out a one liner?Votary
And for the record, if this was actually constant time, we'd have a lot of NP-hard problems to reduce. IIRC, pow is $O(\log(n))$.Villiform
V
10

This is a non-recursive (anonymous) memoizing one 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]
Votary answered 8/2, 2011 at 17:26 Comment(0)
B
7
fib = lambda n, x=0, y=1 : x if not n else fib(n-1, y, x+y)

run time O(n), fib(0) = 0, fib(1) = 1, fib(2) = 1 ...

Bleier answered 18/4, 2011 at 0:25 Comment(0)
P
4

I'm Python newcomer, but did some measure for learning purposes. I've collected some fibo algorithm and took some measure.

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

The result is usually the same. enter image description here

Fiboslow_2 with recursion and cache, Fib integer arithmetic and Fibfast algorithms seems the best ones. Maybe my decorator not the best thing to measure performance, but for an overview it seemed good.

Prudie answered 17/9, 2018 at 20:51 Comment(0)
A
3

Another example, taking the cue from Mark Byers's answer:

fib = lambda n,a=0,b=1: a if n<=0 else fib(n-1,b,a+b)
Allspice answered 8/2, 2011 at 17:30 Comment(2)
...although it seems to have recursion depth problems at n=999. Doesn't Python have tail-recursion?Allspice
No, it doesn't have tail recursion elimination.Until
A
3

Starting Python 3.8, and the introduction of assignment expressions (PEP 572) (:= operator), we can use and update a variable within a list comprehension:

fib = lambda n,x=(0,1):[x := (x[1], sum(x)) for i in range(n+1)][-1][0]

This:

  • Initiates the duo n-1 and n-2 as a tuple x=(0, 1)
  • As part of a list comprehension looping n times, x is updated via an assignment expression (x := (x[1], sum(x))) to the new n-1 and n-2 values
  • Finally, we return from the last iteration, the first part of the x
Ahriman answered 28/4, 2019 at 11:54 Comment(0)
B
3

I wanted to see if I could create an entire sequence, not just the final value.

The following will generate a list of length 100. It excludes the leading [0, 1] and works for both Python2 and Python3. No other lines besides the one!

(lambda i, x=[0,1]: [(x.append(x[y+1]+x[y]), x[y+1]+x[y])[1] for y in range(i)])(100)

Output

[1,
 2,
 3,
 ...
 218922995834555169026,
 354224848179261915075,
 573147844013817084101]
Bridal answered 31/7, 2020 at 21:23 Comment(0)
S
2

Here's an implementation that doesn't use recursion, and only memoizes the last two values instead of the whole sequence history.

nthfib() below is the direct solution to the original problem (as long as imports are allowed)

It's less elegant than using the Reduce methods above, but, although slightly different that what was asked for, it gains the ability to to be used more efficiently as an infinite generator if one needs to output the sequence up to the nth number as well (re-writing slightly as fibgen() below).

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]
Schweinfurt answered 10/6, 2011 at 22:23 Comment(1)
one-liner, but long-liner ;-)Caplan
P
2
def fib(n):
    x =[0,1]
    for i in range(n):
        x=[x[1],x[0]+x[1]]
    return x[0]

take the cue from Jason S, i think my version have a better understanding.

Prolactin answered 14/1, 2014 at 10:40 Comment(1)
the question explicitly asks for a one-liner, did you read this?Caplan
E
1

To solve this problem I got inspired by a similar question here in Stackoverflow Single Statement Fibonacci, and I got this single line function that can output a list of fibonacci sequence. Though, this is a Python 2 script, not tested on 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)

assign this lambda function to a variable to reuse it:

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)

output is a list of fibonacci sequence:

[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Epidemic answered 3/6, 2013 at 1:30 Comment(0)
C
1

I don't know if this is the most pythonic method but this is the best i could come up with:->

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]

The above code doesn't use recursion, just a list to store the values.

Cymbre answered 2/8, 2014 at 17:59 Comment(0)
Y
1

My 2 cents

# One Liner
def nthfibonacci(n):
    return long(((((1+5**.5)/2)**n)-(((1-5**.5)/2)**n))/5**.5)

OR

# 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)
Yerxa answered 25/9, 2014 at 10:26 Comment(0)
S
1

Why not use a list comprehension?

from math import sqrt, floor
[floor(((1+sqrt(5))**n-(1-sqrt(5))**n)/(2**n*sqrt(5))) for n in range(100)]

Without math imports, but less pretty:

[int(((1+(5**0.5))**n-(1-(5**0.5))**n)/(2**n*(5**0.5))) for n in range(100)]
Sheen answered 17/1, 2017 at 21:22 Comment(1)
note that sqrt(5) is the same as 5 ** .5 so you don't need extra math importApril
A
1
sqrt_five = 5 ** .5
phi = (1 + sqrt_five) / 2

calc_fib_number = lambda n: round((phi ** n) / sqrt_five)

fibonnaci_sequence = (calc_fib_number(i) for i in range(1, 26))
print(list(fibonnaci_sequence))

Single line lambda fibonacci but with some extra constants, ** should be O(log(n)) complexity (source: Why is time complexity O(1) for pow(x,y) while it is O(n) for x**y?), note this is a closed form solution for fibonacci. Use the iterator notation to not save every occurence in memory for longer sequences. I think this approach is fit for the case when you don't need a long sequence, but just a single (or a couple non subsequent fibonacci's) with higher index.

The following without ** in function definition, this would require you need subsequent fibonacci numbers

sqrt_five = 5 ** .5
phi = (1 + sqrt_five) / 2
x = 1


def calc_fib_number(n):
    global x
    x *= phi
    return round(x / sqrt_five)


fibonnaci_sequence = (calc_fib_number(i) for i in range(1, 26))
print(list(fibonnaci_sequence))

With addition, you can save with uncommenting dictionary, fit for creating sequence, but for single higher fibonacci number you would have needed to traverse all previous at least for once:

# d = {}


def fibonnaci_sequence(n):
    a, b = 0, 1
    for _ in range(n):
        yield a
        # d[i] = a
        a, b = b, a + b


print(list(fibonnaci_sequence(26)))
April answered 21/12, 2019 at 12:48 Comment(0)
S
0

Similar:

    def fibonacci(n):
      f=[1]+[0]
      for i in range(n):
        f=[sum(f)] + f[:-1]
      print f[1]
Shiah answered 21/2, 2014 at 18:5 Comment(0)
T
0

A simple Fibonacci number generator using recursion

fib = lambda x: 1-x if x < 2 else  fib(x-1)+fib(x-2)
print fib(100)

This takes forever to calculate fib(100) in my computer.

There is also closed form of Fibonacci numbers.

fib = lambda n: int(1/sqrt(5)*((1+sqrt(5))**n-(1-sqrt(5))**n)/2**n)
print fib(50)

This works nearly up to 72 numbers due to precision problem.

Twentyfour answered 8/6, 2015 at 11:14 Comment(0)
V
0

Lambda with logical operators

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)]
Vann answered 9/3, 2016 at 7:10 Comment(0)
P
0

here is how i do it ,however the function returns None for the list comprehension line part to allow me to insert a loop inside .. so basically what it does is appending new elements of the fib seq inside of a list which is over two elements

>>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)
Purtenance answered 29/5, 2016 at 9:54 Comment(0)
G
0

You can generate once a list with some values and use as needed:

fib_fix = []

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)

than for example:

a = fib_fix[76]
Ginger answered 1/3, 2019 at 17:37 Comment(0)
N
0

This is the shortest, perhaps? Used it on leetcode.

x=9**n;return x**-~n//(x*x+~x)%x

Upd. about the same length using pow:

return pow(x:=2<<n,n+1,x*x+~x)%x
Normalcy answered 31/1 at 6:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.