Python Forum

Full Version: My code which adds fibonacci numbers to a list adds too many terms.
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hi, I am new to programming and am self-learning, using projecteuler as a source of questions. I am trying to create a program which generates the fibonacci sequence until the nth term is sufficiently large, and put all the values in a list. My program is as follows:

x = [0, 1]
for j in x:
  if j > 100:
    break
  else:
    x.append(x[-1]+x[-2])
print(x)


In this example, I want it to print out every Fibonacci number less than 100. However, it always prints two more values after my upper bound: in this case, it adds 89 to the list (which I want), followed by 144 and 233. I am wondering if I have a conceptual misunderstanding of how "for _ in _" statement works, as I would've thought it works as follows:
adds first couple of terms fine, then gets to 89, recognises that 89 is less than 100 so adds the next term to the list (144), checks to see if 144 is less than 100, which it isn't, and so does not add 233 to the list.
Apologies if this is a simple mistake, as I said I am new to python.
Thanks.
Use an infinite generator together with itertools.islice.

from itertools import islice


def fib():
    a, b = 0, 1
    while True:
        yield a
        a, b = a + b, a


fibs = list(islice(fib(), 0, 100))
The yield in the function turns the function into a generator.

Generators: https://realpython.com/introduction-to-p...enerators/
Islice: https://docs.python.org/3/library/iterto...ols.islice
(Jun-10-2020, 02:14 PM)DeaD_EyE Wrote: [ -> ]Use an infinite generator together with itertools.islice.

from itertools import islice


def fib():
    a, b = 0, 1
    while True:
        yield a
        a, b = a + b, a


fibs = list(islice(fib(), 0, 100))
The yield in the function turns the function into a generator.

Generators: https://realpython.com/introduction-to-p...enerators/
Islice: https://docs.python.org/3/library/iterto...ols.islice

Hi Dead_EyE, thank you for your response. This is a much nicer way to write out the Fibonnaci numbers compared to how I did it! Even though what you've posted is much nicer, I'd still be interested in knowing why my original code didn't work.
Thanks.
Quote:Even though what you've posted is much nicer, I'd still be interested in knowing why my original code didn't work.
That is because you test on j > 100, but j is never the last item.
Test on "if x[-1]+x[-2] > 100:", that is the last item.

Paul
Misunderstanding. You meant to have values in the list which are lower than 100.
My example creates a list with exactly 100 elements.

You compared j > 100, which is not the final value.
You've to compare (x[-1] + x[-2]) >= 100.

x = [0, 1]
for j in x:
    result = x[-1] + x[-2]
    if result >= 100:
        # stop is result is bigger or equal to 100
        break
    x.append(result)
print(x)
With my provided example:
from itertools import takewhile, islice


def fib():
    a, b = 0, 1
    while True:
        yield a
        a, b = a + b, a


def condition(value):
    return value < 100


generator = fib()
fibs_below_100 = list(takewhile(condition, generator))
And you can combine different functions from itertools.

Not bigger than 50 and maximum 10 elements:
fib_gen = fib()
fibs_below_50 = takewhile(lambda x: x < 100, fib_gen)
fibs_max_10_elements = islice(fibs_below_50 , 0, 10)

final_result = list(fibs_max_10_elements)
There is one big pitfall, if you use infinite generators:
[x for x in fib if x < 100]
This code will run until you get a MemoryError or hit CTRL+C.
There is no stop condition. Only the decision is made to take x or not.
(Jun-10-2020, 02:54 PM)DPaul Wrote: [ -> ]Misunderstanding. You meant to have values in the list which are lower than 100.
My example creates a list with exactly 100 elements.

You compared j > 100, which is not the final value.
You've to compare (x[-1] + x[-2]) >= 100.

Hi, this explanation and the code you have shown me is extremely helpful and I am very grateful. I find the example using "result = x[-1] + x[-2]" especially intuitive. I just have one final question. Why is j not the final value?
Thanks for all your help.
The reason you get two extra numbers in the list is because you are testing x[-3] >. You start with two values in x and j never catches up with the end of the list because each time you test the value of j you add a value to x.

If you want to stop at or before 100, use the DeaD_Eye's test. If you want to stop after the first value >= 100 (kind of what your original code looks like) just use:
x = [0, 1]
while x[-1] < 100:
    x.append(x[-1]+x[-2])
print(x)
Quote: I just have one final question. Why is j not the final value?
I can only explain it the verbose way.
You start with "x = [0,1]", then you say "for j in x:"
So you start with 0 first, which is not the last one, but you are always adding the true last and one-but-last [-1] and [-2]
With the sum you are adding a third item, while the loop now moves to the second one.
So by testing j you are always "late".
Paul