Thenavats
Thenavats

Reputation: 185

How can I create the fibonacci series using a list comprehension?

I am new to python, and I was wondering if I could generate the fibonacci series using python's list comprehension feature. I don't know how list comprehensions are implemented. I tried the following (the intention was to generate the first five fibonacci numbers):

series=[]
series.append(1)
series.append(1)
series += [series[k-1]+series[k-2] for k in range(2,5)]

This piece of code throws the error: IndexError: list index out of range.

Let me know if it is even possible to generate such a series using a list comprehension.

Upvotes: 16

Views: 21039

Answers (14)

Trey Hunner
Trey Hunner

Reputation: 11814

Here's one way that uses an extra third variable to temporarily store the old value of a (since tuple unpacking is not possible with assignment expressions):

>>> a = b = 1
>>> n = 12
>>> fibonacci = [(old:=a, a:=b, b:=old+b)[0] for _ in range(n)]
>>> fibonacci
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144]

And here's a way that avoids making a tuple and then indexing it by instead embedding an assignment expression within another assignment expression:

>>> a = b = 1
>>> n = 12
>>> fibonacci = [(b:=a+(a:=b))-a for _ in range(n)]
>>> fibonacci
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144]

You can even use a generator expression and itertools.count to make an infinitely long generator of every Fibonacci number:

>>> from itertools import count, islice
>>> a = b = 1
>>> numbers = ((b:=a+(a:=b))-a for _ in count())
>>> list(islice(numbers, 12))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144]
>>> list(islice(numbers, 12))
[233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368]

Upvotes: 0

blhsing
blhsing

Reputation: 106543

A simpler approach using assignment expression (outputting the first 10 terms for demo):

n = -1
m = 1
print([m := n + (n := m) for _ in range(10)])

This outputs:

[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

Upvotes: 0

arshewin lal
arshewin lal

Reputation: 1

# Get a number from the user.
number = int(input("enter a number"))

# Create a empty list
mylist=[] 

# create list comprehension following fibonaci series
[mylist.append(0)  if n==0 else mylist.append(1)  if n==1 else mylist.append(mylist[-2]+mylist[-1]) for n in range(number+1)]

print(mylist)  

Upvotes: 0

Camion
Camion

Reputation: 1374

Simplification of @dhassel version (requires python 3.8 or later)

series = [i0 := 0, i1 := 1]+[i1 := i0 + (i0 := i1) for j in range(2, 5)]

One can also be written as a generator expression, but it's a bit tricky because for some reason, the obvious answer: fibo = (v for g in ((i0 := 0, i1 := 1), (i1 := i0 + (i0 := i1) for j in range(2,10))) for v in g) doesn't work (I do not exclude a bug). However, it is OK if you get the subgenerators list outside :

glist = ((i0 := 0, i1 := 1), (i1 := i0 + (i0 := i1) for j in range(2, 5)))
fibo = (v for g in glist for v in g)

Upvotes: 0

Danjar27
Danjar27

Reputation: 39

I did it this way:

def Phi(number:int):
n = [1,1]
[n.append(n[i-2]+n[i-1])for i in range(2,number)]
return n

Upvotes: -1

MSha
MSha

Reputation: 101

From Python One-Liners by Christian Mayer.

n = 10
x = [0,1]
fibs = x[0:2] + [x.append(x[-1] + x[-2]) or x[-1] for i in range(n-2)]
print(fibs)
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

The answer is you can do this with a list comprehension without the assignment operator (works even in Python 2).

Upvotes: 0

dhassell
dhassell

Reputation: 341

Here's a one-line list comprehension solution that avoids the separate initialization step with nested ternary operators and the walrus operator (so needs Python 3.8), and also avoids the rapid onset of overflow problems that the explicit form can give you (with its **n component):

[
    0 if not i else
        (x := [0, 1]) and 1 if i == 1 else
            not x.append(x[-2] + x[-1]) and x[-1]
    for i in range(10)
]

Gives:

[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

This is faster than the explicit form for generating all of the values up to N. If, however, you don't want all of the values then the explicit form could be much faster, but it does suffer from overflow for some N between 1000 and 2000:

n = 2000
int((((1 + 5**0.5) / 2)**n - ((1 - 5**0.5) / 2)**n) / 5**0.5)

gives for me:

OverflowError: (34, 'Numerical result out of range')

whereas the "adding the last two values" approach can generate higher values for larger N. On my machine, I can keep going until some N between 300000 and 400000 before I run out of memory.

Thanks to Jonathan Gregory for leading me most of the way to this approach.

Upvotes: 1

Ziur Olpa
Ziur Olpa

Reputation: 2102

List comprehension of the fibonacci serie, based on the explicit formula 1:

[int((0.5+5**0.5/2)**n/5**0.5+0.5) for n in range(21)]

Upvotes: 0

cs95
cs95

Reputation: 402463

Using Assignment Expression (python >= 3.8):

s = [0, 1]
s += [(s := [s[1], s[0] + s[1]]) and s[1] for k in range(10)]

print (s)
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

Upvotes: 8

nikhil_2017
nikhil_2017

Reputation: 1

Using List comprehension :

n = int(input())
fibonacci_list = [0,1]
[fibonacci_list.append(fibonacci_list[k-1]+fibonacci_list[k-2]) for k in range(2,n)]

if n<=0:
   print('+ve numbers only')
elif n == 1:
   fibonacci_list = [fibonacci_list[0]]
   print(fibonacci_list)
else:
   print(fibonacci_list)

maybe it's a feasible solution for this problem...

Upvotes: -1

cdlane
cdlane

Reputation: 41872

We could write it as a clean Python list comprehension (or generator) using it's relationship to the golden ratio:

>>> series = [int((((1 + 5**0.5) / 2)**n - ((1 - 5**0.5) / 2)**n) / 5**0.5) for n in range(1, 21)]
>>> series
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
>>> 

or a little more nicely as:

>>> square_root_of_five = 5**0.5
>>> Phi = (1 + square_root_of_five) / 2
>>> phi = (1 - square_root_of_five) / 2
>>> 
>>> series = [int((Phi**n - phi**n) / square_root_of_five) for n in range(1, 21)]
>>> series
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]

Upvotes: 10

Bill Bell
Bill Bell

Reputation: 21643

If you know how many terms of the series you will need then you can write the code compactly without a list comprehension like this.

def Fibonacci(n):
    f0, f1 = 1, 1
    for _ in range(n):
        yield f0
        f0, f1 = f1, f0+f1

fibs = list(Fibonacci(10))
print (fibs)

If you want some indefinite number of terms then you could use this, which is very similar.

def Fibonacci():
    f0, f1 = 1, 1
    while True:
        yield f0
        f0, f1 = f1, f0+f1

fibs = []
for f in Fibonacci():
    fibs.append(f)
    if f>100:
        break
print (fibs)

When you need a potentially infinite collection of items you should perhaps consider either a function with one or more yield statements or a generator expression. I'd love to be able to make Fibonacci numbers with a generator expression but apparently one can't.

Upvotes: 8

daphtdazz
daphtdazz

Reputation: 8159

To build on what Willem van Onsem said:

The conventional way to calculate the nth term of the fibonacci sequence is to sum the n-1 and n-2 terms, as you're aware. A list comprehension is designed to create a list with no side effects during the comprehension (apart from the creation of the single list). Storing the last 2 terms of the sequence during calculation of the sequence is a side-effect, therefore a list comprehension is ill-suited to the task on its own.

A safe way around this would be to make a closure generator (essentially a generator with some associated private state) that can be passed to the list comprehension such that the list comprehension does not have to worry about the details of what's being stored:

def fib_generator(n):

    def fib_n_generator():
        last = 1
        curr = 1

        if n == 0:
            return

        yield last
        if n == 1:
            return

        yield curr
        if n == 2:
            return

        ii = 2
        while ii < n:
            next = curr + last
            yield next
            last = curr
            curr = next
            ii += 1

    return fib_n_generator()

fib = [xx for xx in fib_generator(10)]
print(fib)

Upvotes: 5

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476594

You cannot do it like that: the list comprehension is evaluated first, and then that list is added to series. So basically it would be like you would have written:

series=[]
series.append(1)
series.append(1)
temp = [series[k-1]+series[k-2] for k in range(2,5)]
series += temp

You can however solve this by using list comprehension as a way to force side effects, like for instance:

series=[]
series.append(1)
series.append(1)
[series.append(series[k-1]+series[k-2]) for k in range(2,5)]

Note that we here do not add the result to series. The list comprehension is only used such that .append is called on series. However some consider list comprehensions with side effects rather error prone: it is not very declarative and tends to introduce bugs if not done carefully.

Upvotes: 13

Related Questions