16

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.

martineau
  • 119,623
  • 25
  • 170
  • 301
Thenavats
  • 175
  • 1
  • 1
  • 5

13 Answers13

13

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.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • 1
    [Using assignment expressions in python3.8](https://stackoverflow.com/a/64549057/4909087), you can bypass the list comprehension with side effects to create a new list of fib numbers. Whether it's better than using side effects (as you've done here) is debatable. – cs95 Oct 27 '20 at 06:08
  • This creates another list. I would rather use a normal function. – chaulap Jun 27 '21 at 11:00
10

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]
cdlane
  • 40,441
  • 5
  • 32
  • 81
8

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.

Bill Bell
  • 21,021
  • 5
  • 43
  • 58
8

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]
cs95
  • 379,657
  • 97
  • 704
  • 746
5

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)
daphtdazz
  • 7,754
  • 34
  • 54
  • Thanks for the explanation in the first para that states `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`. – Iqra. May 19 '20 at 11:07
  • However, even after spending 15+ mins I cannot understand what's the benefit of using yield in above code snippet. `fib = [xx for xx in fib_generator(10)]` would have still be called if `fib_generator(n)` be a function w/o generator. – Iqra. May 19 '20 at 11:39
  • 1
    The yield is crucial, else it's not a generator. Without the yield `fib_n_generator()` would just return one thing, not an iterable of things. I could have made my answer simpler though: I didn't need to nest the function, so it should have looked like Bill Bell's answer (which is why his has more upvotes ;-) ). I could also have re-written it to return a list instead of a generator, but that would defeat the main purpose of using a generator, which is to avoid unnecessary RAM usage. – daphtdazz May 19 '20 at 13:19
1

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.

dhassell
  • 341
  • 2
  • 5
  • Whoaw did not know that, thanks for sharing! Looks like the correct answer to me ! – Tirbo06 Jul 29 '22 at 12:37
  • Even simpler (or at least shorter ;-) ) : `series = [i0 := 0, i1 := 1]+[i1 := i0 + (i0 := i1) for j in range(2,5)]` – Camion Nov 02 '22 at 18:38
0

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)]
Ziur Olpa
  • 1,839
  • 1
  • 12
  • 27
0

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

MSha
  • 101
  • 1
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Oct 12 '21 at 20:38
0

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
Danjar27
  • 31
  • 1
  • 2
0

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)
Camion
  • 1,264
  • 9
  • 22
0
# 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)  
4b0
  • 21,981
  • 30
  • 95
  • 142
0

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]
blhsing
  • 91,368
  • 6
  • 71
  • 106
-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...