Python generators
Today I came across this wonderful little curiosity.
I was going to write a little Python program to try it out. Basically, we are creating two fibonacci sequences, one starting with 12, 18 and the other starting with 5, 5. At first I thought there will be nothing new here. Then I remembered that I was looking for a good example with which to demonstrate generator functions and I figured out that this would be a perfect example. Basically, we are looking for a function that each time we call it will generate the next fibonacci number in a sequence.
There is an example of such a Fibonacci function in an earlier post which I will repeat here:
def fibonacci():
parent, baby = 1, 0
while baby < 1000:
print baby
parent, baby = (parent + baby, parent)
In this case, we don’t want to print the fibonacci number, but want to return the next number in the series. We can do this with a generator function. A generator function is one that has the keyword yield. yield is like a return statement, except that the function remembers where we are and then the next time we call that function, we start from where we left off (or, in other words, directly after yield).
Here is our fibonacci generator:
def fibonacci(t1, t2):
while True:
yield t1
t1, t2 = t2, t1 + t2
fib = fibonacci(0, 1)
for i in range(10):
print fib.next()
Running this we get:
0
1
1
2
3
5
8
13
21
34
Now comes the interesting part - we can create as many generators as we want, so here is our final program:
def fibonacci(t1, t2):
while True:
yield t1
t1, t2 = t2, t1 + t2
lhs = fibonacci(12, 18)
rhs = fibonacci(5, 5)
for i in range(30):
l = lhs.next()
r = rhs.next()
print '%10d %10d %8.7f' % (l, r, float(l) / r)
So, when I run it, I get the following results:
12 5 2.4000000
18 5 3.6000000
30 10 3.0000000
48 15 3.2000000
78 25 3.1200000
126 40 3.1500000
204 65 3.1384615
330 105 3.1428571
534 170 3.1411765
864 275 3.1418182
This looks promising and it seems to converge really quickly. So, lets run it for 20 iterations.
12 5 2.4000000
18 5 3.6000000
30 10 3.0000000
48 15 3.2000000
78 25 3.1200000
126 40 3.1500000
204 65 3.1384615
330 105 3.1428571
534 170 3.1411765
864 275 3.1418182
1398 445 3.1415730
2262 720 3.1416667
3660 1165 3.1416309
5922 1885 3.1416446
9582 3050 3.1416393
15504 4935 3.1416413
25086 7985 3.1416406
40590 12920 3.1416409
65676 20905 3.1416408
106266 33825 3.1416408
Well, that’s weird. It got really close to Pi (which, as far as I can remember is something like 3.1415927), but then it seems to wander off again. Well, maybe it oscillates for a while before settling down. So, let’s try 30 iterations (I will just record the last few lines of output).
728358 231840 3.1416408
1178508 375125 3.1416408
1906866 606965 3.1416408
3085374 982090 3.1416408
4992240 1589055 3.1416408
8077614 2571145 3.1416408
13069854 4160200 3.1416408
Well, it seems that we’re not converging on Pi, but something very close to Pi.
Maybe I should have just stopped after 10 iterations. I marvelled at how the ratio between two fibonacci sequences would converge on Pi. Now I am a bit wiser, but also a bit sadder.